├── .eslintrc.yml ├── .eslintignore ├── packages ├── edge-express │ ├── .env │ ├── .babelrc │ ├── src │ │ ├── index.js │ │ ├── addErrorMiddleware.js │ │ ├── addCoreMiddleware.js │ │ ├── addFallbackHandler.js │ │ ├── createExpressServer.js │ │ └── addSecurityMiddleware.js │ ├── package.json │ └── readme.md ├── edge-boilerplate │ ├── src │ │ ├── Init.js │ │ ├── Variables.css │ │ ├── views │ │ │ ├── Missing │ │ │ │ ├── Missing.css │ │ │ │ └── Missing.js │ │ │ ├── Counter │ │ │ │ ├── Counter.css │ │ │ │ └── Counter.js │ │ │ ├── Localization │ │ │ │ ├── Localization.css │ │ │ │ └── Localization.js │ │ │ └── Home │ │ │ │ ├── Home.css │ │ │ │ └── Home.js │ │ ├── components │ │ │ ├── htmlhead │ │ │ │ ├── favicon.ico │ │ │ │ ├── favicon-16x16.png │ │ │ │ ├── favicon-32x32.png │ │ │ │ ├── mstile-150x150.png │ │ │ │ ├── apple-touch-icon.png │ │ │ │ ├── android-chrome-192x192.png │ │ │ │ ├── android-chrome-512x512.png │ │ │ │ ├── manifest.webmanifest │ │ │ │ ├── HtmlHead.js │ │ │ │ └── safari-pinned-tab.svg │ │ │ ├── view │ │ │ │ ├── ErrorPlaceholder.module.css │ │ │ │ ├── LoadingPlaceholder.js │ │ │ │ ├── ErrorPlaceholder.js │ │ │ │ └── ViewWrapper.js │ │ │ ├── Story.test.js │ │ │ ├── navigation │ │ │ │ ├── Navigation.story.js │ │ │ │ └── Navigation.js │ │ │ └── __snapshots__ │ │ │ │ └── Story.test.js.snap │ │ ├── fonts │ │ │ └── source-sans-pro │ │ │ │ ├── SourceSansPro-It.otf.woff │ │ │ │ ├── SourceSansPro-Bold.otf.woff │ │ │ │ ├── SourceSansPro-It.otf.woff2 │ │ │ │ ├── SourceSansPro-Black.otf.woff │ │ │ │ ├── SourceSansPro-Black.otf.woff2 │ │ │ │ ├── SourceSansPro-BlackIt.otf.woff │ │ │ │ ├── SourceSansPro-Bold.otf.woff2 │ │ │ │ ├── SourceSansPro-BoldIt.otf.woff │ │ │ │ ├── SourceSansPro-BoldIt.otf.woff2 │ │ │ │ ├── SourceSansPro-Light.otf.woff │ │ │ │ ├── SourceSansPro-Light.otf.woff2 │ │ │ │ ├── SourceSansPro-LightIt.otf.woff │ │ │ │ ├── SourceSansPro-Regular.otf.woff │ │ │ │ ├── SourceSansPro-BlackIt.otf.woff2 │ │ │ │ ├── SourceSansPro-LightIt.otf.woff2 │ │ │ │ ├── SourceSansPro-Regular.otf.woff2 │ │ │ │ ├── SourceSansPro-Semibold.otf.woff │ │ │ │ ├── SourceSansPro-Semibold.otf.woff2 │ │ │ │ ├── SourceSansPro-ExtraLight.otf.woff │ │ │ │ ├── SourceSansPro-ExtraLight.otf.woff2 │ │ │ │ ├── SourceSansPro-ExtraLightIt.otf.woff │ │ │ │ ├── SourceSansPro-SemiboldIt.otf.woff │ │ │ │ ├── SourceSansPro-SemiboldIt.otf.woff2 │ │ │ │ ├── SourceSansPro-ExtraLightIt.otf.woff2 │ │ │ │ └── SourceSansPro.css │ │ ├── Init.css │ │ ├── Application.css │ │ ├── dev.js │ │ ├── messages │ │ │ ├── en.json │ │ │ ├── de.json │ │ │ ├── es.json │ │ │ └── fr.json │ │ ├── Responsive.css │ │ ├── client │ │ │ └── index.js │ │ ├── modules │ │ │ ├── Env.js │ │ │ └── Counter.js │ │ ├── binary.js │ │ ├── State.js │ │ ├── server │ │ │ └── index.js │ │ └── Application.js │ ├── .babelrc │ ├── postcss.config.js │ ├── jest.config.js │ ├── .env │ ├── .env-dev │ ├── browserslist │ ├── hooks │ │ └── webpack.js │ ├── .edgerc.yml │ └── package.json ├── edge-webpack │ ├── .gitignore │ ├── .babelrc │ ├── __tests__ │ │ └── project │ │ │ ├── src │ │ │ ├── client │ │ │ │ └── index.js │ │ │ ├── components │ │ │ │ └── logo │ │ │ │ │ ├── Logo.js │ │ │ │ │ └── logo.svg │ │ │ └── App.js │ │ │ ├── project.test.js │ │ │ └── __snapshots__ │ │ │ └── project.test.js.snap │ ├── jest.config.js │ ├── src │ │ ├── index.test.js │ │ ├── modules │ │ │ ├── Environment.js │ │ │ ├── Static.js │ │ │ ├── Locales.js │ │ │ ├── Optimization.js │ │ │ └── Experience.js │ │ ├── config.js │ │ └── index.js │ ├── readme.md │ └── package.json ├── edge-useragent │ ├── .babelrc │ ├── test │ │ ├── mocha.opts │ │ ├── fixtures │ │ │ └── static.custom.yaml │ │ ├── satisfies.test.js │ │ └── parser.qa.js │ ├── static │ │ ├── user_agent.before.yaml │ │ └── user_agent.after.yaml │ ├── bin │ │ ├── update.js │ │ └── testfiles.js │ ├── credits.md │ └── package.json ├── edge-builder │ ├── .babelrc │ ├── src │ │ ├── index.js │ │ ├── plugins │ │ │ ├── Status.js │ │ │ ├── ChunkNames.js │ │ │ └── VerboseProgress.js │ │ ├── commands │ │ │ └── build.js │ │ ├── webpack │ │ │ └── dev.js │ │ └── binary.js │ ├── readme.md │ └── package.json ├── edge-common │ ├── .babelrc │ ├── src │ │ ├── index.js │ │ └── logging.js │ └── package.json ├── edge-postcss │ ├── .babelrc │ ├── __tests__ │ │ ├── fixtures │ │ │ ├── import-a.css │ │ │ ├── import-b.css │ │ │ ├── import-c.css │ │ │ ├── other │ │ │ │ └── Home.css │ │ │ ├── formula.png │ │ │ ├── americano.png │ │ │ └── font │ │ │ │ ├── SourceSansPro-Light.otf.woff │ │ │ │ ├── SourceSansPro-Light.otf.woff2 │ │ │ │ └── SourceSansPro.css │ │ ├── __snapshots__ │ │ │ ├── merge.js.snap │ │ │ ├── mediaqueries.js.snap │ │ │ ├── optimization.js.snap │ │ │ ├── sass.js.snap │ │ │ ├── assets.js.snap │ │ │ ├── color.js.snap │ │ │ ├── effects.js.snap │ │ │ ├── fixes.js.snap │ │ │ ├── layout.js.snap │ │ │ └── extensions.js.snap │ │ ├── merge.js │ │ ├── mediaqueries.js │ │ ├── optimization.js │ │ ├── effects.js │ │ ├── extensions.js │ │ ├── sass.js │ │ ├── assets.js │ │ ├── color.js │ │ ├── fixes.js │ │ ├── layout.js │ │ └── core.js │ ├── browserslist │ └── package.json ├── edge-storybook │ ├── src │ │ ├── babelrc │ │ ├── addons.js │ │ ├── storyshot.js │ │ ├── webpack.config.js │ │ └── config.js │ ├── .babelrc │ ├── package.json │ └── readme.md ├── edge-style │ ├── postcss.config.js │ ├── src │ │ ├── Normalize.css │ │ ├── index.js │ │ ├── BoxSizing.css │ │ ├── Reset.css │ │ ├── Aria.css │ │ ├── Intl.css │ │ ├── OpenType.css │ │ └── Sanitize.css │ ├── package.json │ └── readme.md ├── edge-core │ ├── .babelrc │ ├── browserslist │ ├── src │ │ ├── client │ │ │ ├── updateState.js │ │ │ ├── renderApp.js │ │ │ └── getBrowserLocale.js │ │ ├── server │ │ │ ├── prepareResponse.js │ │ │ ├── getLocaleData.js │ │ │ ├── renderApplication.js │ │ │ └── renderPage.js │ │ ├── common │ │ │ ├── ApolloClient.test.js │ │ │ ├── fetchData.js │ │ │ ├── deepFetch.js │ │ │ ├── State.test.js │ │ │ ├── createKernel.js │ │ │ ├── wrapApplication.js │ │ │ ├── ApolloClient.js │ │ │ ├── Intl.js │ │ │ └── State.js │ │ ├── client.js │ │ ├── common.js │ │ └── server.js │ ├── readme.md │ └── package.json └── jest-preset-edge │ ├── test.js │ ├── transform │ ├── graphql.js │ ├── css.js │ ├── babel.js │ └── file.js │ ├── package.json │ ├── jest-preset.js │ ├── readme.md │ └── setup.js ├── .stylelintrc.yml ├── .gitignore ├── .editorconfig ├── lerna.json ├── .prettierrc.yml ├── appveyor.yml ├── package.json ├── .travis.yml └── .gitattributes /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | extends: 2 | - readable 3 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | packages/edge-useragent/src/*.js 2 | -------------------------------------------------------------------------------- /packages/edge-express/.env: -------------------------------------------------------------------------------- 1 | SERVER_PORT = 1339 2 | -------------------------------------------------------------------------------- /.stylelintrc.yml: -------------------------------------------------------------------------------- 1 | extends: 2 | - stylelint-config-readable 3 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/Init.js: -------------------------------------------------------------------------------- 1 | import "./Init.css" 2 | -------------------------------------------------------------------------------- /packages/edge-webpack/.gitignore: -------------------------------------------------------------------------------- 1 | .cache-loader 2 | dist 3 | -------------------------------------------------------------------------------- /packages/edge-useragent/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ "edge" ] 3 | } 4 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "edge" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/edge-builder/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "edge" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/edge-common/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "edge" 4 | ] 5 | } 6 | 7 | -------------------------------------------------------------------------------- /packages/edge-express/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "edge" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/edge-postcss/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "edge" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/fixtures/import-a.css: -------------------------------------------------------------------------------- 1 | .root { 2 | color: #f00; 3 | } 4 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/fixtures/import-b.css: -------------------------------------------------------------------------------- 1 | .section { 2 | color: #00d; 3 | } 4 | -------------------------------------------------------------------------------- /packages/edge-storybook/src/babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "edge" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/edge-style/postcss.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | module.exports = require("edge-postcss") 3 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/postcss.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | module.exports = require("edge-postcss") 3 | -------------------------------------------------------------------------------- /packages/edge-core/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ "edge", { "target": "current" }] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/fixtures/import-c.css: -------------------------------------------------------------------------------- 1 | .image { 2 | background: url("./formula.png"); 3 | } 4 | -------------------------------------------------------------------------------- /packages/jest-preset-edge/test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | test("Empty test", () => { 3 | // empty 4 | }) 5 | -------------------------------------------------------------------------------- /packages/edge-useragent/test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter spec 2 | --ui bdd 3 | --require should 4 | --require @babel/register 5 | -------------------------------------------------------------------------------- /packages/edge-webpack/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ "edge", { "target": "node", "modules": false } ] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/jest.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | module.exports = { 3 | "preset": "jest-preset-edge" 4 | } 5 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/Variables.css: -------------------------------------------------------------------------------- 1 | $red: #bf2f2f; 2 | $gray-dark: #4e4d4d; 3 | $gray-light: #f7f7f7; 4 | $full-site-padding: 1rem; 5 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/fixtures/other/Home.css: -------------------------------------------------------------------------------- 1 | .preloader { 2 | background: url("./logo.svg"); 3 | border: 1px solid #000; 4 | } 5 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/views/Missing/Missing.css: -------------------------------------------------------------------------------- 1 | @import "../../Variables.css"; 2 | 3 | .root { 4 | padding: 2rem; 5 | 6 | color: $red; 7 | } 8 | -------------------------------------------------------------------------------- /packages/edge-common/src/index.js: -------------------------------------------------------------------------------- 1 | export { NAME, VERSION, LOGPREFIX, SCHEMA, loadConfig } from "./config" 2 | export { notify, colorize } from "./logging" 3 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/fixtures/formula.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-postcss/__tests__/fixtures/formula.png -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/fixtures/americano.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-postcss/__tests__/fixtures/americano.png -------------------------------------------------------------------------------- /packages/edge-postcss/browserslist: -------------------------------------------------------------------------------- 1 | and_chr >= 50 2 | chrome >= 50 3 | edge >= 12 4 | firefox >= 45 5 | ie >= 10 6 | ios_saf >= 10 7 | safari >= 10 8 | samsung >= 4 9 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/views/Counter/Counter.css: -------------------------------------------------------------------------------- 1 | @import "../../Variables.css"; 2 | @import "../../Responsive.css"; 3 | 4 | .root { 5 | /* nothing */ 6 | } 7 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/components/htmlhead/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/components/htmlhead/favicon.ico -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/views/Localization/Localization.css: -------------------------------------------------------------------------------- 1 | @import "../../Variables.css"; 2 | @import "../../Responsive.css"; 3 | 4 | .root { 5 | /* nothing */ 6 | } 7 | -------------------------------------------------------------------------------- /packages/edge-webpack/__tests__/project/src/client/index.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { render } from "react-dom" 3 | import App from "../App" 4 | 5 | render() 6 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/.env: -------------------------------------------------------------------------------- 1 | SERVER_PORT = 1339 2 | 3 | ENABLE_CSP = false 4 | ENABLE_NONCE = false 5 | 6 | API_URL = http://localhost:7339 7 | APOLLO_URL = http://localhost:8339 8 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/.env-dev: -------------------------------------------------------------------------------- 1 | SERVER_PORT = 1339 2 | 3 | ENABLE_CSP = false 4 | ENABLE_NONCE = false 5 | 6 | API_URL = http://localhost:7339 7 | APOLLO_URL = http://localhost:8339 8 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/components/htmlhead/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/components/htmlhead/favicon-16x16.png -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/components/htmlhead/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/components/htmlhead/favicon-32x32.png -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/components/view/ErrorPlaceholder.module.css: -------------------------------------------------------------------------------- 1 | .log { 2 | margin-top: 20px; 3 | padding: 10px; 4 | 5 | background: #f4f2f4; 6 | font-size: 12px; 7 | } 8 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/components/htmlhead/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/components/htmlhead/mstile-150x150.png -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/components/htmlhead/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/components/htmlhead/apple-touch-icon.png -------------------------------------------------------------------------------- /packages/edge-style/src/Normalize.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This depends on postcss-normalize to select the amount of normalization 3 | * required by the projects browserslist. 4 | */ 5 | @import-normalize; 6 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/fixtures/font/SourceSansPro-Light.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-postcss/__tests__/fixtures/font/SourceSansPro-Light.otf.woff -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/components/htmlhead/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/components/htmlhead/android-chrome-192x192.png -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/components/htmlhead/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/components/htmlhead/android-chrome-512x512.png -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-It.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-It.otf.woff -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/fixtures/font/SourceSansPro-Light.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-postcss/__tests__/fixtures/font/SourceSansPro-Light.otf.woff2 -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Bold.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Bold.otf.woff -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-It.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-It.otf.woff2 -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Black.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Black.otf.woff -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Black.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Black.otf.woff2 -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-BlackIt.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-BlackIt.otf.woff -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Bold.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Bold.otf.woff2 -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-BoldIt.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-BoldIt.otf.woff -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-BoldIt.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-BoldIt.otf.woff2 -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Light.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Light.otf.woff -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Light.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Light.otf.woff2 -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-LightIt.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-LightIt.otf.woff -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Regular.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Regular.otf.woff -------------------------------------------------------------------------------- /packages/edge-style/src/index.js: -------------------------------------------------------------------------------- 1 | import "./Normalize.css" 2 | import "./Reset.css" 3 | import "./Sanitize.css" 4 | import "./BoxSizing.css" 5 | import "./Aria.css" 6 | import "./OpenType.css" 7 | import "./Intl.css" 8 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-BlackIt.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-BlackIt.otf.woff2 -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-LightIt.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-LightIt.otf.woff2 -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Regular.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Regular.otf.woff2 -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Semibold.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Semibold.otf.woff -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Semibold.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-Semibold.otf.woff2 -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-ExtraLight.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-ExtraLight.otf.woff -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-ExtraLight.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-ExtraLight.otf.woff2 -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-ExtraLightIt.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-ExtraLightIt.otf.woff -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-SemiboldIt.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-SemiboldIt.otf.woff -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-SemiboldIt.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-SemiboldIt.otf.woff2 -------------------------------------------------------------------------------- /packages/edge-webpack/__tests__/project/src/components/logo/Logo.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import logoUrl from "./logo.svg" 3 | 4 | export default function Logo() { 5 | return Alt Text 6 | } 7 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-ExtraLightIt.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastian-software/edge/HEAD/packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro-ExtraLightIt.otf.woff2 -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/components/Story.test.js: -------------------------------------------------------------------------------- 1 | import { dirname } from "path" 2 | import initStoryshots from "@storybook/addon-storyshots" 3 | 4 | initStoryshots({ 5 | configPath: dirname(require.resolve("edge-storybook")) 6 | }) 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | 4 | *debug.log 5 | *error.log 6 | .cache 7 | .DS_Store 8 | .vs 9 | 10 | package-lock.json 11 | 12 | packages/*/lib 13 | packages/*/build 14 | packages/*/bin 15 | packages/*/docs 16 | 17 | *.log 18 | -------------------------------------------------------------------------------- /packages/edge-webpack/__tests__/project/src/App.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Logo from "./components/logo/Logo" 3 | 4 | export default function App() { 5 | return ( 6 |
7 | 8 |
9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /packages/edge-core/browserslist: -------------------------------------------------------------------------------- 1 | [production] 2 | safari >= 10 3 | ios >= 10 4 | edge >= 13 5 | chrome >= 50 6 | firefox >= 50 7 | ie >= 11 8 | 9 | [development] 10 | safari >= 10.1 11 | ios >= 10.3 12 | edge >= 15 13 | chrome >= 58 14 | firefox >= 53 15 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/browserslist: -------------------------------------------------------------------------------- 1 | [production] 2 | safari >= 10 3 | ios >= 10 4 | edge >= 13 5 | chrome >= 50 6 | firefox >= 50 7 | ie >= 11 8 | 9 | [development] 10 | safari >= 10.1 11 | ios >= 10.3 12 | edge >= 15 13 | chrome >= 60 14 | firefox >= 56 15 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/components/navigation/Navigation.story.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { storiesOf } from "@storybook/react" 3 | import Navigation from "./Navigation" 4 | 5 | storiesOf("Navigation", module) 6 | .add("basic demo", () => ) 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | quote_type = double 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /packages/edge-webpack/jest.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-commonjs */ 2 | module.exports = { 3 | preset: "jest-preset-edge", 4 | verbose: true, 5 | testPathIgnorePatterns: [ 6 | "/node_modules/", 7 | "/dist/", 8 | "/__tests__/.*/src" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/hooks/webpack.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/unambiguous, import/no-commonjs */ 2 | module.exports = function webpack(config, { isServer, isClient, isProduction, isDevelopment }) { 3 | // Extend and manipulate the Webpack configuration here. 4 | return config 5 | } 6 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/__snapshots__/merge.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Import Basic 1`] = ` 4 | ".root{ 5 | color:red 6 | }" 7 | `; 8 | 9 | exports[`Import with Merge 1`] = ` 10 | ".section{ 11 | color:#00d; 12 | background:#333 13 | }" 14 | `; 15 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/fixtures/font/SourceSansPro.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | src: 3 | url("./SourceSansPro-Light.otf.woff2") format("woff2"), 4 | url("./SourceSansPro-Light.otf.woff") format("woff"); 5 | 6 | font-family: Source Sans Pro; 7 | font-weight: 300; 8 | font-style: normal; 9 | } 10 | -------------------------------------------------------------------------------- /packages/jest-preset-edge/transform/graphql.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-commonjs */ 2 | 3 | const loader = require("graphql-tag/loader") 4 | 5 | module.exports = { 6 | process(source) { 7 | return loader.call({ 8 | cacheable() { 9 | // empty 10 | } 11 | }, source) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/edge-core/src/client/updateState.js: -------------------------------------------------------------------------------- 1 | import { createRootReducer } from "../common/State" 2 | 3 | export default function updateState(NextState, kernel) { 4 | console.log("[EDGE]: Updating application state...") 5 | kernel.store.replaceReducer( 6 | createRootReducer(NextState.getReducers(), kernel.router) 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /packages/edge-useragent/static/user_agent.before.yaml: -------------------------------------------------------------------------------- 1 | user_agent_parsers: 2 | # Rival IQ crawler/bot 3 | - regex: '(Rival IQ, rivaliq.com)' 4 | family_replacement: 'Rival IQ' 5 | 6 | device_parsers: 7 | # Rival IQ crawler/bot 8 | - regex: '(Rival IQ, rivaliq.com)' 9 | device_replacement: 'Spider' 10 | 11 | os_parsers: -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "2.5.1", 3 | "packages": ["packages/*"], 4 | "version": "independent", 5 | "npmClient": "yarn", 6 | "useWorkspaces": true, 7 | "changelog": { 8 | "labels": { 9 | "enhancement": ":rocket: Enhancement", 10 | "bug": ":bug: Bug Fix" 11 | }, 12 | "cacheDir": ".cache/changelog" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/views/Home/Home.css: -------------------------------------------------------------------------------- 1 | @import "../../Variables.css"; 2 | @import "../../Responsive.css"; 3 | 4 | .preloader { 5 | width: calc(width("./logo.svg") / 2); 6 | height: calc(width("./logo.svg") / 2); 7 | 8 | background: url("./logo.svg"); 9 | border: 1px solid #000; 10 | } 11 | 12 | .intro { 13 | font-size: 20px; 14 | } 15 | -------------------------------------------------------------------------------- /packages/edge-storybook/src/addons.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */ 2 | 3 | import "@storybook/addon-actions/register" 4 | import "@storybook/addon-links/register" 5 | import "@storybook/addon-viewport/register" 6 | import "@storybook/addon-jest/register" 7 | import "@storybook/addon-storysource/register" 8 | -------------------------------------------------------------------------------- /packages/edge-core/src/server/prepareResponse.js: -------------------------------------------------------------------------------- 1 | import { parse } from "edge-useragent" 2 | 3 | import getLocaleData from "./getLocaleData" 4 | 5 | export default function prepareResponse(request) { 6 | const intl = getLocaleData(request) 7 | const browser = parse(request.headers["user-agent"]) 8 | 9 | return { 10 | intl, 11 | browser 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/edge-builder/src/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | cleanClient, 3 | cleanServer, 4 | buildClient, 5 | buildServer 6 | } from "./commands/build" 7 | import { connectWithWebpack, createMiddleware } from "./webpack/dev" 8 | 9 | export { 10 | cleanClient, 11 | cleanServer, 12 | buildClient, 13 | buildServer, 14 | connectWithWebpack, 15 | createMiddleware 16 | } 17 | -------------------------------------------------------------------------------- /packages/edge-core/src/common/ApolloClient.test.js: -------------------------------------------------------------------------------- 1 | import { createApolloClient } from "./ApolloClient" 2 | 3 | test("Create Apollo Client - No Data", () => { 4 | expect(createApolloClient()).toBeDefined() 5 | }) 6 | 7 | test("Create Apollo Client - With Initial Data and URL", () => { 8 | expect(createApolloClient({ uri: "http://my.apollo.uri" })).toBeDefined() 9 | }) 10 | -------------------------------------------------------------------------------- /packages/edge-useragent/static/user_agent.after.yaml: -------------------------------------------------------------------------------- 1 | user_agent_parsers: 2 | # command line tools 3 | - regex: '(Wget)/(\d+)\.(\d+)\.?([ab]?\d+[a-z]*)' 4 | 5 | - regex: '(curl)/(\d+)\.(\d+)\.(\d+)' 6 | family_replacement: 'cURL' 7 | 8 | os_parsers: 9 | # See #33 for matching the iOS AFHTTPClient 10 | - regex: '(iOS) (\d+)\.(\d+)(?:\.(\d+))?' 11 | 12 | device_parsers: -------------------------------------------------------------------------------- /packages/edge-webpack/src/index.test.js: -------------------------------------------------------------------------------- 1 | import { core, full } from "./index" 2 | import webpack from "webpack" 3 | 4 | test("Webpack parses core config", () => { 5 | const compiler = webpack(core()) 6 | expect(compiler).toBeDefined() 7 | }) 8 | 9 | test("Webpack parses full config", () => { 10 | const compiler = webpack(full()) 11 | expect(compiler).toBeDefined() 12 | }) 13 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/components/view/LoadingPlaceholder.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { FormattedMessage } from "react-intl" 3 | 4 | export default function LoadingPlaceholder() { 5 | return ( 6 | <> 7 |

8 |

9 | 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /packages/edge-core/src/client/renderApp.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { hydrate } from "react-dom" 3 | 4 | import wrapApplication from "../common/wrapApplication" 5 | 6 | export default function renderApp(Application, kernel) { 7 | console.log("[EDGE]: Rendering application...") 8 | hydrate(wrapApplication(, kernel), document.getElementById("root")) 9 | } 10 | -------------------------------------------------------------------------------- /packages/edge-express/src/index.js: -------------------------------------------------------------------------------- 1 | export { default as addCoreMiddleware } from "./addCoreMiddleware" 2 | export { default as addErrorMiddleware } from "./addErrorMiddleware" 3 | export { default as addFallbackHandler } from "./addFallbackHandler" 4 | export { default as addSecurityMiddleware } from "./addSecurityMiddleware" 5 | export { default as createExpressServer } from "./createExpressServer" 6 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/__snapshots__/mediaqueries.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Custom Media 1`] = ` 4 | "@media (max-width:30em){ 5 | body{ 6 | font-size:12px 7 | } 8 | }" 9 | `; 10 | 11 | exports[`Media Query Min/Max 1`] = ` 12 | "@media screen and (min-width:500px) and (max-width:1200px){ 13 | .elem{ 14 | display:block 15 | } 16 | }" 17 | `; 18 | -------------------------------------------------------------------------------- /packages/edge-style/src/BoxSizing.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable selector-max-type, selector-max-universal */ 2 | 3 | /** 4 | * Add box sizing inheritence in all browsers (opinionated). 5 | */ 6 | 7 | *, 8 | ::before, 9 | ::after { 10 | box-sizing: inherit; 11 | } 12 | 13 | /** 14 | * Add border box sizing in all browsers (opinionated). 15 | */ 16 | 17 | html { 18 | box-sizing: border-box; 19 | } 20 | -------------------------------------------------------------------------------- /packages/edge-webpack/src/modules/Environment.js: -------------------------------------------------------------------------------- 1 | import { getEnvironment } from "universal-dotenv" 2 | import webpack from "webpack" 3 | 4 | import { IS_PRODUCTION, BUILD_TARGET } from "../config" 5 | 6 | export default { 7 | mode: IS_PRODUCTION ? "production" : "development", 8 | name: BUILD_TARGET, 9 | 10 | plugins: [ 11 | new webpack.DefinePlugin(getEnvironment().webpack) 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/edge-webpack/readme.md: -------------------------------------------------------------------------------- 1 | # Common Webpack Utility Belt 2 | 3 | ## Features 4 | 5 | - Support for React, GraphQL, CSS Modules, ... 6 | - Tweaked for nice user experience. 7 | - Configuration via environment variables using [Universal Dotenv](https://www.npmjs.com/package/universal-dotenv). 8 | - Enabled for using multi threading and advanced caching. 9 | - Able to generate static HTML with SRI support. 10 | -------------------------------------------------------------------------------- /packages/edge-useragent/test/fixtures/static.custom.yaml: -------------------------------------------------------------------------------- 1 | test_cases: 2 | 3 | - user_agent_string: 'curl/7.12.1 (i686-redhat-linux-gnu) libcurl/7.12.1 OpenSSL/0.9.7a zlib/1.2.1.2 libidn/0.5.6' 4 | family: 'cURL' 5 | major: '7' 6 | minor: '12' 7 | patch: '1' 8 | 9 | - user_agent_string: 'Wget/1.10.1 (Red Hat modified)' 10 | family: 'Wget' 11 | major: '1' 12 | minor: '10' 13 | patch: '1' 14 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/views/Missing/Missing.js: -------------------------------------------------------------------------------- 1 | import PropTypes from "prop-types" 2 | import React from "react" 3 | 4 | import Styles from "./Missing.css" 5 | 6 | function Missing() { 7 | return ( 8 |
9 |

Sorry, that page was not found.

10 |
11 | ) 12 | } 13 | 14 | Missing.propTypes = { 15 | intl: PropTypes.object 16 | } 17 | 18 | export default Missing 19 | -------------------------------------------------------------------------------- /packages/jest-preset-edge/transform/css.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-commonjs */ 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/en/webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return "module.exports = {};" 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return "cssTransform" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/Init.css: -------------------------------------------------------------------------------- 1 | @import "./Variables.css"; 2 | @import "./fonts/source-sans-pro/SourceSansPro.css"; 3 | 4 | /* stylelint-disable selector-max-type, selector-max-universal */ 5 | 6 | /* 7 | apply a natural box layout model to all elements, but allowing 8 | components to change 9 | */ 10 | 11 | html { 12 | font-family: Source Sans Pro, sans-serif; 13 | } 14 | 15 | body { 16 | background: $gray-light; 17 | } 18 | -------------------------------------------------------------------------------- /packages/edge-useragent/bin/update.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Update our definition file. 3 | */ 4 | import { update } from "../src/update" 5 | update((err, data) => { 6 | if (err) { 7 | console.error("Update unsuccessfull due to reasons") 8 | console.log(err.message) 9 | console.log(err.stack) 10 | 11 | return 12 | } 13 | console.log( 14 | "Successfully fetched and generated new parsers from the internets." 15 | ) 16 | }) 17 | -------------------------------------------------------------------------------- /packages/jest-preset-edge/transform/babel.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-commonjs */ 2 | 3 | // Import .env files to make it possible to access environment variables inside tests. 4 | require("universal-dotenv") 5 | 6 | // Babel transformation but with the possibility to add test-specific plugins/presets. 7 | const babelJest = require("babel-jest") 8 | 9 | module.exports = babelJest.createTransformer({ 10 | // any specific babel settings 11 | }) 12 | -------------------------------------------------------------------------------- /packages/edge-storybook/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "development": { 4 | "presets": [ 5 | [ 6 | "edge", 7 | { 8 | "modules": "cjs", 9 | "env": "production" 10 | } 11 | ] 12 | ] 13 | }, 14 | "test": { 15 | "presets": [ 16 | [ 17 | "edge", 18 | { 19 | "target": "test" 20 | } 21 | ] 22 | ] 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/edge-core/src/common/fetchData.js: -------------------------------------------------------------------------------- 1 | import deepFetch from "./deepFetch" 2 | 3 | export default async function fetchData(App) { 4 | // Asynchronous magic... loading required application data 5 | // Supports parallel loading of either Apollo-style (aka fetchData()) 6 | const start = new Date() 7 | console.log("[EDGE] Fetching data...") 8 | const result = await deepFetch(App) 9 | console.log(`[EDGE] Done in ${new Date() - start}ms`) 10 | 11 | return result 12 | } 13 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/merge.js: -------------------------------------------------------------------------------- 1 | import { compile } from "./core" 2 | 3 | // ==================================================== 4 | // ================== MERGING ========================= 5 | // ==================================================== 6 | 7 | test("Import Basic", () => 8 | compile("@import './fixtures/import-a.css';") 9 | ) 10 | 11 | test("Import with Merge", () => 12 | compile("@import './fixtures/import-b.css'; .section { background: #333; }") 13 | ) 14 | -------------------------------------------------------------------------------- /packages/edge-style/src/Reset.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable selector-max-type */ 2 | 3 | /* 4 | * Reset margins and paddings on all block level elements 5 | */ 6 | 7 | html, 8 | body, 9 | p, 10 | ol, 11 | ul, 12 | li, 13 | dl, 14 | dt, 15 | dd, 16 | blockquote, 17 | figure, 18 | fieldset, 19 | legend, 20 | textarea, 21 | pre, 22 | iframe, 23 | hr, 24 | h1, 25 | h2, 26 | h3, 27 | h4, 28 | h5, 29 | h6, 30 | button, 31 | input, 32 | select { 33 | padding: 0; 34 | margin: 0; 35 | } 36 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/components/htmlhead/manifest.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Edge", 3 | "icons": [ 4 | { 5 | "src": "/android-chrome-192x192.png", 6 | "sizes": "192x192", 7 | "type": "image/png" 8 | }, 9 | { 10 | "src": "/android-chrome-512x512.png", 11 | "sizes": "512x512", 12 | "type": "image/png" 13 | } 14 | ], 15 | "theme_color": "#ffffff", 16 | "background_color": "#ffffff", 17 | "display": "standalone" 18 | } 19 | -------------------------------------------------------------------------------- /packages/edge-storybook/src/storyshot.js: -------------------------------------------------------------------------------- 1 | import initStoryshots, { 2 | multiSnapshotWithOptions 3 | } from "@storybook/addon-storyshots" 4 | 5 | initStoryshots({ 6 | // Delegate to centralized config 7 | configPath: __dirname, 8 | 9 | // Storing seperate snapshots for each individual story 10 | test: multiSnapshotWithOptions({}), 11 | 12 | // Ignore all containers, only snapshot pure components 13 | // without any app logic or data fetching 14 | storyKindRegex: /^((?!container).)*$/i 15 | }) 16 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/components/navigation/Navigation.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Link } from "@reach/router" 3 | 4 | export default function Navigation() { 5 | return ( 6 | 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/views/Home/Home.js: -------------------------------------------------------------------------------- 1 | import Helmet from "react-helmet" 2 | import React from "react" 3 | 4 | import Styles from "./Home.css" 5 | 6 | export default function Home() { 7 | return ( 8 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /packages/edge-webpack/__tests__/project/src/components/logo/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/edge-webpack/src/modules/Static.js: -------------------------------------------------------------------------------- 1 | import HtmlWebpackPlugin from "html-webpack-plugin" 2 | import SriPlugin from "webpack-subresource-integrity" 3 | 4 | import { IS_PRODUCTION } from "../config" 5 | 6 | export default { 7 | plugins: [ 8 | IS_PRODUCTION ? 9 | new SriPlugin({ 10 | hashFuncNames: [ "sha256", "sha512" ], 11 | enabled: IS_PRODUCTION 12 | }) : 13 | null, 14 | 15 | new HtmlWebpackPlugin({ 16 | inject: true, 17 | title: process.env.APP_TITLE || null 18 | }) 19 | ].filter(Boolean) 20 | } 21 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/.edgerc.yml: -------------------------------------------------------------------------------- 1 | entry: 2 | serverMain: src/server/index.js 3 | clientMain: src/client/index.js 4 | serverVendor: src/server/vendor.js 5 | clientVendor: src/client/vendor.js 6 | 7 | build: 8 | enableSourceMaps: true 9 | bundleCompression: uglify 10 | useCacheLoader: true 11 | babelEnvPrefix: edge 12 | 13 | locale: 14 | default: de-DE 15 | supported: [ de-DE, de-AT, de-CH, fr-CH, en-US ] 16 | 17 | output: 18 | server: build/server 19 | client: build/client 20 | public: /static/ 21 | 22 | hook: 23 | webpack: hooks/webpack.js 24 | -------------------------------------------------------------------------------- /packages/edge-core/src/client.js: -------------------------------------------------------------------------------- 1 | // This file is just for exporting infrastructure to applications built upon this. 2 | 3 | // Polyfill for fetch() API 4 | // https://github.com/developit/unfetch 5 | import "unfetch/polyfill" 6 | 7 | // Polyfill for RequestAnimationFrame which is required since React v16 8 | import "raf/polyfill" 9 | 10 | export * from "./common" 11 | 12 | export { default as getBrowserLocale } from "./client/getBrowserLocale" 13 | export { default as renderApp } from "./client/renderApp" 14 | export { default as updateState } from "./client/updateState" 15 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/__snapshots__/optimization.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`CSS-O (Optimizer) 1`] = ` 4 | ".elem{ 5 | color:red 6 | }" 7 | `; 8 | 9 | exports[`Calc Keep 1`] = ` 10 | ".elem{ 11 | width:calc(100px + 2%) 12 | }" 13 | `; 14 | 15 | exports[`Calc Trivial 1`] = ` 16 | ".elem{ 17 | width:310px 18 | }" 19 | `; 20 | 21 | exports[`Calc Variable 1`] = ` 22 | ".elem{ 23 | width:210px 24 | }" 25 | `; 26 | 27 | exports[`zIndex 1`] = ` 28 | ".first{ 29 | z-index:1 30 | } 31 | 32 | .second{ 33 | z-index:2 34 | }" 35 | `; 36 | -------------------------------------------------------------------------------- /packages/edge-useragent/test/satisfies.test.js: -------------------------------------------------------------------------------- 1 | describe("useragent/satisfies", () => { 2 | const useragent = require("../src"), 3 | ua = 4 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_1) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.24 Safari/535.2" 5 | 6 | describe("#satisfies", () => { 7 | it("should satisfy that range selector", () => { 8 | const agent = useragent.parse(ua) 9 | 10 | agent.satisfies("15.x || >=19.5.0 || 25.0.0 - 17.2.3").should.be_true 11 | agent.satisfies(">16.12.0").should.be_false 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/mediaqueries.js: -------------------------------------------------------------------------------- 1 | import { compile } from "./core" 2 | 3 | // ==================================================== 4 | // =============== MEDIA QUERIES ====================== 5 | // ==================================================== 6 | 7 | test("Media Query Min/Max", () => 8 | compile("@media screen and (width >= 500px) and (width <= 1200px) { .elem { display: block; } }") 9 | ) 10 | 11 | test("Custom Media", () => 12 | compile(` 13 | @custom-media --small-viewport (max-width: 30em); 14 | @media (--small-viewport) { 15 | body { font-size: 12px; } 16 | } 17 | `) 18 | ) 19 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/Application.css: -------------------------------------------------------------------------------- 1 | @import "./Variables.css"; 2 | 3 | .root { 4 | &::after { 5 | content: ""; 6 | 7 | position: absolute; 8 | top: 0; 9 | bottom: 0; 10 | left: 0; 11 | right: 0; 12 | z-index: 1000; 13 | 14 | background: rgba($gray-dark, 0.5); 15 | transition: opacity ease-out 200ms, transform linear 0ms 200ms; 16 | will-change: transform, opacity; 17 | } 18 | } 19 | 20 | .alive { 21 | &::after { 22 | opacity: 0; 23 | transform: scale(0); 24 | } 25 | } 26 | 27 | .content { 28 | min-height: 20rem; 29 | 30 | background: $gray-light; 31 | } 32 | -------------------------------------------------------------------------------- /packages/edge-express/src/addErrorMiddleware.js: -------------------------------------------------------------------------------- 1 | import PrettyError from "pretty-error" 2 | 3 | const pretty = new PrettyError() 4 | 5 | // this will skip events.js and http.js and similar core node files 6 | pretty.skipNodeFiles() 7 | 8 | // this will skip all the trace lines about express` core and sub-modules 9 | pretty.skipPackage("express") 10 | 11 | export default function addErrorMiddleware(server) { 12 | // and use it for our app's error handler: 13 | server.use((error, request, response, next) => { 14 | // eslint-disable-line max-params 15 | console.log(pretty.render(error)) 16 | next() 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/components/view/ErrorPlaceholder.js: -------------------------------------------------------------------------------- 1 | import PropTypes from "prop-types" 2 | import React from "react" 3 | import { FormattedMessage } from "react-intl" 4 | 5 | import Styles from "./ErrorPlaceholder.module.css" 6 | 7 | export default function ErrorPlaceholder({ error }) { 8 | return ( 9 | <> 10 |

11 |

12 |
13 |         {error.message}
14 |       
15 | 16 | ) 17 | } 18 | 19 | Error.propTypes = { 20 | error: PropTypes.object 21 | } 22 | -------------------------------------------------------------------------------- /.prettierrc.yml: -------------------------------------------------------------------------------- 1 | # We generally allow wider code lines, but for auto formatting this 2 | # is quite a good rule to produce readable code. 3 | printWidth: 80 4 | 5 | # Use two spaces for tabs 6 | tabWidth: 2 7 | 8 | # Unify with convention used in JSX, HTML and CSS to use double quotes 9 | singleQuote: false 10 | 11 | # Don't use semicolons where they are not required 12 | semi: false 13 | 14 | # Don't do stupid trailing commas reducing noise ratio. 15 | trailingComma: none 16 | 17 | # More space is better for readability 18 | bracketSpacing: true 19 | 20 | # Put the > of a multi-line JSX element at the end of the last line 21 | jsxBracketSameLine: false 22 | -------------------------------------------------------------------------------- /packages/edge-core/src/common/deepFetch.js: -------------------------------------------------------------------------------- 1 | import reactTreeWalker from "react-tree-walker" 2 | 3 | /* eslint-disable no-shadow */ 4 | /* eslint-disable max-params */ 5 | export default function deepFetch(rootElement) { 6 | function visitor(element, instance, context) { 7 | if (instance && typeof instance.fetchData === "function") { 8 | const value = instance.fetchData(); 9 | if (value instanceof Promise) { 10 | return value.catch((err) => { 11 | console.log("[EDGE] Fetch failed: " + err); 12 | }) 13 | } 14 | } 15 | 16 | return true 17 | } 18 | 19 | return reactTreeWalker(rootElement, visitor) 20 | } 21 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/components/__snapshots__/Story.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Storyshots Navigation basic demo 1`] = ` 4 | 32 | `; 33 | -------------------------------------------------------------------------------- /packages/jest-preset-edge/transform/file.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-commonjs */ 2 | 3 | const path = require("path") 4 | 5 | // This is a custom Jest transformer turning file imports into filenames. 6 | // http://facebook.github.io/jest/docs/en/webpack.html 7 | 8 | module.exports = { 9 | process(src, filename) { 10 | const assetFilename = JSON.stringify(path.basename(filename)) 11 | 12 | if (filename.match(/\.svg$/)) { 13 | return `module.exports = { 14 | __esModule: true, 15 | default: ${assetFilename}, 16 | ReactComponent: () => ${assetFilename}, 17 | };` 18 | } 19 | 20 | return `module.exports = ${assetFilename};` 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/__snapshots__/sass.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Calc Complex 1`] = ` 4 | "h1{ 5 | width:calc(29.9997% - 22px) 6 | }" 7 | `; 8 | 9 | exports[`Calc Simple 1`] = ` 10 | "h1{ 11 | width:13% 12 | }" 13 | `; 14 | 15 | exports[`Nested Basic 1`] = ` 16 | "body h1{ 17 | font-weight:700 18 | } 19 | 20 | body h2{ 21 | font-weight:400 22 | }" 23 | `; 24 | 25 | exports[`Nested Parent Selector 1`] = ` 26 | "body ul li:first-child{ 27 | margin-top:0 28 | }" 29 | `; 30 | 31 | exports[`Sassy Mixins 1`] = ` 32 | "h1{ 33 | color:#ff4136 34 | }" 35 | `; 36 | 37 | exports[`Sassy Variables 1`] = ` 38 | "h1{ 39 | background:#ff4136 40 | }" 41 | `; 42 | -------------------------------------------------------------------------------- /packages/edge-style/src/Aria.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Change the cursor on busy elements (opinionated). 3 | */ 4 | 5 | [aria-busy="true"] { 6 | cursor: progress; 7 | } 8 | 9 | /* 10 | * Change the cursor on control elements (opinionated). 11 | */ 12 | 13 | [aria-controls] { 14 | cursor: pointer; 15 | } 16 | 17 | /* 18 | * Change the display on visually hidden accessible elements (opinionated). 19 | */ 20 | 21 | [aria-hidden="false"][hidden]:not(:focus) { 22 | position: absolute; 23 | display: inherit; 24 | clip: rect(0, 0, 0, 0); 25 | } 26 | 27 | /* 28 | * Change the cursor on disabled, not-editable, or otherwise 29 | * inoperable elements (opinionated). 30 | */ 31 | 32 | [aria-disabled] { 33 | cursor: default; 34 | } 35 | -------------------------------------------------------------------------------- /packages/edge-core/src/common.js: -------------------------------------------------------------------------------- 1 | export { 2 | createReduxStore, 3 | createRootReducer, 4 | emptyReducer, 5 | emptyMiddleware, 6 | emptyEnhancer, 7 | edgeReducer 8 | } from "./common/State" 9 | 10 | export { createApolloClient } from "./common/ApolloClient" 11 | 12 | export { 13 | requiresIntlPolyfill, 14 | installIntlPolyfill, 15 | requiresReactIntl, 16 | installReactIntl, 17 | getRegion, 18 | getLanguage, 19 | getLocale 20 | } from "./common/Intl" 21 | 22 | export { default as wrapApplication } from "./common/wrapApplication" 23 | export { default as deepFetch } from "./common/deepFetch" 24 | export { default as createKernel } from "./common/createKernel" 25 | export { default as fetchData } from "./common/fetchData" 26 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/components/view/ViewWrapper.js: -------------------------------------------------------------------------------- 1 | import PropTypes from "prop-types" 2 | import React from "react" 3 | 4 | import ErrorPlaceholder from "./ErrorPlaceholder" 5 | import LoadingPlaceholder from "./LoadingPlaceholder" 6 | 7 | export default function ViewWrapper(props) { 8 | const { Component, loading, error, ownProps } = props 9 | if (loading) { 10 | return 11 | } 12 | 13 | if (error) { 14 | return 15 | } 16 | 17 | return Component ? : null 18 | } 19 | 20 | ViewWrapper.propTypes = { 21 | Component: PropTypes.func, 22 | loading: PropTypes.bool, 23 | error: PropTypes.object, 24 | ownProps: PropTypes.object 25 | } 26 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # http://www.appveyor.com/docs/appveyor-yml 2 | 3 | clone_depth: 10 4 | 5 | environment: 6 | matrix: 7 | - nodejs_version: 6 8 | - nodejs_version: 8 9 | - nodejs_version: 10 10 | 11 | platform: 12 | - x86 13 | 14 | matrix: 15 | fast_finish: true 16 | 17 | version: "{build}" 18 | build: off 19 | deploy: off 20 | 21 | install: 22 | - ps: Install-Product node $env:nodejs_version $env:platform 23 | - yarn install --ignore-engines 24 | 25 | test_script: 26 | - yarn test 27 | 28 | notifications: 29 | - provider: Slack 30 | incoming_webhook: 31 | secure: 42qYgf76P/QjYb5QJ18gFFJ67iTlXc1QzEva9BFofwh5KzpF/QJ/pvRark1i3aux6uwL53zdDKTjXfy8Ozpqqk9DhnU5M1oA96bQYWOnmZU= 32 | 33 | cache: 34 | - "%LOCALAPPDATA%\\Yarn" 35 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/optimization.js: -------------------------------------------------------------------------------- 1 | import { compile } from "./core" 2 | 3 | 4 | // ==================================================== 5 | // ================ OPTIMIZATION ====================== 6 | // ==================================================== 7 | 8 | test("Calc Trivial", () => 9 | compile(".elem { width: calc(300px + 10px); }") 10 | ) 11 | 12 | test("Calc Variable", () => 13 | compile("$margin: 10px; .elem { width: calc(200px + $margin); }") 14 | ) 15 | 16 | test("Calc Keep", () => 17 | compile(".elem { width: calc(100px + 2%); }") 18 | ) 19 | 20 | test("zIndex", () => 21 | compile(".first { z-index: 1000; } .second { z-index: 2000; }") 22 | ) 23 | 24 | test("CSS-O (Optimizer)", () => 25 | compile(".elem { color: #ff0000; }") 26 | ) 27 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/dev.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { connectWithWebpack, createMiddleware } from "edge-builder" 4 | import { createExpressServer } from "edge-express" 5 | import { loadConfig } from "edge-common" 6 | import "universal-dotenv" 7 | 8 | async function main() { 9 | const { config } = await loadConfig() 10 | const { middleware, multiCompiler } = createMiddleware(config) 11 | 12 | const server = createExpressServer({ 13 | staticConfig: { 14 | public: config.output.public, 15 | path: config.output.client 16 | }, 17 | localeConfig: config.locale, 18 | afterSecurity: [], 19 | beforeFallback: [ ...middleware ] 20 | }) 21 | 22 | connectWithWebpack(server, multiCompiler) 23 | } 24 | 25 | process.nextTick(main) 26 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/messages/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "generic.error.title": "Error", 3 | "generic.info.title": "Info", 4 | 5 | "lifecycle.error": "The data could not be loaded.", 6 | "lifecycle.loading": "Loading data...", 7 | "lifecycle.skip": "Cannot load the data because a required parameter is missing.", 8 | 9 | "router.notfound": "The page was unfortunately not found!", 10 | "router.codesplit.error": "Unfortunately, an error occurred during the loading process: ", 11 | "router.codesplit.loading": "The requested page is loaded...", 12 | 13 | "locale.value.auto": "Automatic", 14 | "locale.value.de-DE": "German (Germany)", 15 | "locale.value.fr-FR": "Français (France)", 16 | "locale.value.es-ES": "Espagnol (Espagne)", 17 | "locale.value.en-US": "English (USA)" 18 | } 19 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/effects.js: -------------------------------------------------------------------------------- 1 | import { compile } from "./core" 2 | 3 | // ==================================================== 4 | // ================== EFFECTS ========================= 5 | // ==================================================== 6 | 7 | test("Magic Animations", () => 8 | compile(".animation { animation-name: magic; }") 9 | ) 10 | 11 | test("Will Change Compat", () => 12 | compile(".scaled { will-change: width; }") 13 | ) 14 | 15 | test("Easings", () => 16 | compile(".snake { transition: all 600ms ease-in-sine; }") 17 | ) 18 | 19 | test("Pleeease Filters", () => 20 | compile(".box { filter: drop-shadow(16px 16px 20px blue); }") 21 | ) 22 | 23 | test("Transform Shortcut", () => 24 | compile(".transform { scale: 2; translate: 10px 20px; }") 25 | ) 26 | 27 | -------------------------------------------------------------------------------- /packages/edge-useragent/credits.md: -------------------------------------------------------------------------------- 1 | The regex library that the useragent parser uses if from; code.google.com/p/ua-parser/ 2 | which is released under Apache license: 3 | 4 | # Copyright 2009 Google Inc. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | #     http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/messages/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "generic.error.title": "Fehler", 3 | "generic.info.title": "Information", 4 | 5 | "lifecycle.error": "Die Daten konnten nicht geladen werden.", 6 | "lifecycle.loading": "Lade Daten...", 7 | "lifecycle.skip": "Kann die Daten nicht laden, da ein benötigter Parameter fehlt.", 8 | 9 | "router.notfound": "Die Seite wurde leider nicht gefunden!", 10 | "router.codesplit.error": "Leider ist während des Ladevorgangs ein Fehler aufgetreten: ", 11 | "router.codesplit.loading": "Die angeforderte Seite wird geladen...", 12 | 13 | "locale.value.auto": "Automatisch", 14 | "locale.value.de-DE": "Deutsch (Deutschland)", 15 | "locale.value.fr-FR": "Français (France)", 16 | "locale.value.es-ES": "Espagnol (Espagne)", 17 | "locale.value.en-US": "English (USA)" 18 | } 19 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/messages/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "generic.error.title": "Desperfecto", 3 | "generic.info.title": "Inteligencia", 4 | 5 | "lifecycle.error": "No se han podido cargar los datos.", 6 | "lifecycle.loading": "Carga de datos...", 7 | "lifecycle.skip": "No se pueden cargar los datos porque falta un parámetro requerido.", 8 | 9 | "router.notfound": "Desafortunadamente la página no fue encontrada!", 10 | "router.codesplit.error": "Desafortunadamente, un error ocurrió durante el proceso de carga: ", 11 | "router.codesplit.loading": "Se carga la página solicitada...", 12 | 13 | "locale.value.auto": "Automático", 14 | "locale.value.de-DE": "Deutsch (Deutschland)", 15 | "locale.value.fr-FR": "Français (France)", 16 | "locale.value.es-ES": "Espagnol (Espagne)", 17 | "locale.value.en-US": "English (USA)" 18 | } 19 | -------------------------------------------------------------------------------- /packages/edge-builder/src/plugins/Status.js: -------------------------------------------------------------------------------- 1 | export default class Status 2 | { 3 | constructor({ name, start, done }) 4 | { 5 | this.name = name 6 | 7 | this.start = start 8 | this.done = done 9 | 10 | this.watcher = null 11 | this.running = false 12 | } 13 | 14 | checkDone = () => { 15 | this.running = false 16 | this.done() 17 | } 18 | 19 | apply(compiler) 20 | { 21 | compiler.plugin("done", (stats) => { 22 | if (!stats.hasErrors()) { 23 | this.watcher = setTimeout(this.checkDone, 0) 24 | } 25 | }) 26 | 27 | compiler.plugin("compile", (stats) => { 28 | if (this.watcher) { 29 | clearTimeout(this.watcher) 30 | } 31 | 32 | if (!this.running) { 33 | this.running = true 34 | this.start() 35 | } 36 | }) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/edge-useragent/bin/testfiles.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import request from "request" 4 | import path from "path" 5 | import fs from "fs" 6 | 7 | const files = { 8 | "pgts.yaml": 9 | "https://raw.githubusercontent.com/ua-parser/uap-core/master/test_resources/pgts_browser_list.yaml", 10 | "firefoxes.yaml": 11 | "https://raw.githubusercontent.com/ua-parser/uap-core/master/test_resources/firefox_user_agent_strings.yaml" 12 | } 13 | 14 | /** 15 | * Update the fixtures 16 | */ 17 | 18 | Object.keys(files).forEach(key => { 19 | request(files[key], function response(err, res, data) { 20 | if (err || res.statusCode !== 200) return console.error("failed to update") 21 | 22 | console.log("downloaded", files[key]) 23 | fs.writeFileSync(path.join(__dirname, "..", "test", "fixtures", key), data) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/extensions.js: -------------------------------------------------------------------------------- 1 | import { compile } from "./core" 2 | 3 | // ==================================================== 4 | // ================= EXTENSIONS ======================= 5 | // ==================================================== 6 | 7 | test("Responsive Type", () => 8 | compile("html { font-size: responsive 12px 21px; font-range: 420px 1280px; }") 9 | ) 10 | 11 | test("Clearfix", () => 12 | compile(".row { clear: fix; }") 13 | ) 14 | 15 | test("System UI", () => 16 | compile("body { font-family: system-ui; }") 17 | ) 18 | 19 | test("Normalize.css", () => 20 | compile(".before { color: red; } @import-normalize; .after { color: green; }") 21 | ) 22 | 23 | test("Initial", () => 24 | compile("h1 { font-family: initial; }") 25 | ) 26 | 27 | test("Initial - All", () => 28 | compile("ul { all: initial; }") 29 | ) 30 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/messages/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "generic.error.title": "Défaut", 3 | "generic.info.title": "Renseignement", 4 | 5 | "lifecycle.error": "Les données n'ont pas pu être chargées.", 6 | "lifecycle.loading": "Chargement des données...", 7 | "lifecycle.skip": "Impossible de charger les données parce qu'il manque un paramètre requis.", 8 | 9 | "router.notfound": "La page n'a malheureusement pas été trouvée!", 10 | "router.codesplit.error": "Malheureusement, une erreur s'est produite pendant le processus de chargement.: ", 11 | "router.codesplit.loading": "La page demandée est chargée...", 12 | 13 | "locale.value.auto": "Automatique", 14 | "locale.value.de-DE": "Deutsch (Deutschland)", 15 | "locale.value.fr-FR": "Français (France)", 16 | "locale.value.es-ES": "Espagnol (Espagne)", 17 | "locale.value.en-US": "English (USA)" 18 | } 19 | -------------------------------------------------------------------------------- /packages/edge-webpack/src/config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | import "universal-dotenv" 3 | 4 | export const IS_PRODUCTION = process.env.NODE_ENV === "production" 5 | export const IS_TEST = process.env.NODE_ENV === "test" 6 | export const IS_DEVELOPMENT = !IS_PRODUCTION && !IS_TEST 7 | 8 | export const BUILD_TARGET = process.env.BUILD_TARGET || "client" 9 | 10 | export const ENABLE_SOURCE_MAPS = true 11 | 12 | export const ASSET_EXTS = /\.(eot|woff|woff2|ttf|otf|svg|png|jpg|jpeg|jp2|jpx|jxr|gif|webp|mp4|mp3|ogg|pdf|html|ico|xml)$/ 13 | export const BABEL_EXTS = /\.(js|mjs|jsx)$/ 14 | export const POSTCSS_EXTS = /\.(css|sss|pcss)$/ 15 | export const POSTCSS_MODULE_EXTS = /\.module\.(css|sss|pcss)$/ 16 | export const COMPRESSABLE_EXTS = /\.(ttf|otf|svg|pdf|html|ico|txt|md|html|js|css|json|xml)$/ 17 | export const YAML_EXTS = /\.(yml|yaml)$/ 18 | export const GRAPHQL_EXTS = /\.(graphql|gql)$/ 19 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/Responsive.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable selector-max-type, property-no-unknown */ 2 | 3 | @mixin mobile { 4 | @media screen and (width >= 20em) and (width < 30em) { 5 | @content; 6 | } 7 | } 8 | 9 | @mixin desktop { 10 | @media screen and (width >= 48em) { 11 | @content; 12 | } 13 | } 14 | 15 | /* 16 | Adaptive Sizing for Mobile 17 | See also: https://github.com/seaneking/postcss-responsive-type 18 | */ 19 | @include mobile { 20 | html { 21 | /* min-size, max-size */ 22 | font-size: responsive 16px 18px; 23 | 24 | /* viewport widths between which font-size is fluid */ 25 | font-range: 320px 400px; 26 | } 27 | } 28 | 29 | @include desktop { 30 | html { 31 | /* min-size, max-size */ 32 | font-size: responsive 16px 18px; 33 | 34 | /* viewport widths between which font-size is fluid */ 35 | font-range: 1280px 1600px; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/sass.js: -------------------------------------------------------------------------------- 1 | import { compile } from "./core" 2 | 3 | // ==================================================== 4 | // ================ SASS INSPIRED ===================== 5 | // ==================================================== 6 | 7 | test("Sassy Mixins", () => 8 | compile("@mixin simple{ color: red; } h1 { @include simple; }") 9 | ) 10 | 11 | test("Sassy Variables", () => 12 | compile("$bgColor: red; h1 { background: $bgColor; }") 13 | ) 14 | 15 | test("Nested Basic", () => 16 | compile("body { h1 { font-weight: bold; } h2 { font-weight: normal; }}") 17 | ) 18 | 19 | test("Nested Parent Selector", () => 20 | compile("body { ul { li { &:first-child { margin-top: 0; }}}}") 21 | ) 22 | 23 | test("Calc Simple", () => 24 | compile("h1 { width: calc(10% + 3%); }") 25 | ) 26 | 27 | test("Calc Complex", () => 28 | compile("h1 { width: calc(99.999% * 3 / 10 - 22px); }") 29 | ) 30 | -------------------------------------------------------------------------------- /packages/jest-preset-edge/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jest-preset-edge", 3 | "version": "0.2.1-alpha.18", 4 | "description": "Centralized Jest configuration for Edge-based projects.", 5 | "main": "jest-preset.js", 6 | "scripts": { 7 | "test": "jest", 8 | "prepack": "echo" 9 | }, 10 | "files": [ 11 | "setup.js", 12 | "transform" 13 | ], 14 | "author": { 15 | "name": "Sebastian Software", 16 | "email": "s.werner@sebastian-software.de", 17 | "url": "https://www.sebastian-software.de" 18 | }, 19 | "license": "Apache-2.0", 20 | "dependencies": { 21 | "babel-jest": "^23.6.0", 22 | "graphql-tag": "^2.9.2", 23 | "identity-obj-proxy": "^3.0.0", 24 | "jest": "^23.6.0", 25 | "jest-canvas-mock": "^1.1.0", 26 | "jest-fetch-mock": "^1.6.6", 27 | "jest-mock-now": "^1.2.0", 28 | "raf": "^3.4.0", 29 | "universal-dotenv": "^1.9.1", 30 | "url-polyfill": "^1.1.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/edge-core/src/server/getLocaleData.js: -------------------------------------------------------------------------------- 1 | export default function getLocaleData(request) { 2 | let locale = request.locale 3 | let language = null 4 | let region = null 5 | let source = null 6 | 7 | if (locale) 8 | { 9 | language = locale.language 10 | region = locale.region 11 | source = locale.source 12 | locale = `${language}-${region}` 13 | } 14 | else 15 | { 16 | console.warn("Locale not auto-detected by server!") 17 | 18 | locale = process.env.DEFAULT_LOCALE 19 | if (locale) { 20 | source = "env" 21 | let splitted = locale.split("-") 22 | language = splitted[0] 23 | region = splitted[1] 24 | } else { 25 | locale = "en-US" 26 | language = "en" 27 | region = "US" 28 | source = "default" 29 | } 30 | } 31 | 32 | console.log(`Using locale: ${locale} via ${source}`) 33 | 34 | return { 35 | locale, 36 | language, 37 | region 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/client/index.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { hydrate, render } from "react-dom" 3 | 4 | import Application from "../Application" 5 | import State from "../State" 6 | 7 | // eslint-disable-next-line no-console 8 | console.log(`[APP] Build: ${process.env.NODE_ENV}-${process.env.BUILD_TARGET}`) 9 | 10 | 11 | const root = document.getElementById("root") 12 | hydrate(, root) 13 | 14 | 15 | 16 | 17 | if (process.env.NODE_ENV === "development" && module.hot) { 18 | // Any changes to our application will cause a hotload re-render. 19 | module.hot.accept("../Application", () => { 20 | const NextApplication = require("../Application").default 21 | render(, root) 22 | }) 23 | 24 | // Any changes to our state machinery will update the reducers. 25 | module.hot.accept("../State", () => { 26 | const NextState = require("../State").default 27 | updateState(NextState) 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "devDependencies": { 4 | "eslint": "^5.6.0", 5 | "eslint-config-readable": "^2.2.0", 6 | "flow-bin": "^0.81.0", 7 | "lerna": "^3.4.0", 8 | "lerna-changelog": "^0.8.0", 9 | "lint-staged": "^7.2.2", 10 | "prettier": "^1.14.2", 11 | "stylelint": "^9.5.0", 12 | "stylelint-config-readable": "^1.4.0" 13 | }, 14 | "scripts": { 15 | "clean": "lerna clean --yes && rimraf node_modules", 16 | "update": "ncu -ua && lerna exec ncu -- -ua", 17 | "test": "lerna run --stream --sort test", 18 | "pretest": "npm run prepack && lerna link", 19 | "xprecommit": "lint-staged", 20 | "prepack": "lerna run --stream --sort prepack", 21 | "publish": "npm run prepack && lerna publish" 22 | }, 23 | "pre-commit": "lint-staged", 24 | "lint-staged": { 25 | "*.js": "eslint --quiet", 26 | "*.css": "stylelint --quiet" 27 | }, 28 | "workspaces": [ 29 | "packages/*" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /packages/edge-express/src/addCoreMiddleware.js: -------------------------------------------------------------------------------- 1 | import compression from "compression" 2 | import createLocaleMiddleware from "express-locale" 3 | import cookieParser from "cookie-parser" 4 | import bodyParser from "body-parser" 5 | 6 | export default function addCoreMiddleware(server, { locale }) { 7 | // Parse cookies via standard express tooling 8 | server.use(cookieParser()) 9 | 10 | // Detect client locale and match it with configuration 11 | server.use( 12 | createLocaleMiddleware({ 13 | priority: [ "query", "cookie", "accept-language", "default" ], 14 | default: locale.default.replace(/-/, "_"), 15 | allowed: locale.supported.map(entry => entry.replace(/-/, "_")) 16 | }) 17 | ) 18 | 19 | // Parse application/x-www-form-urlencoded 20 | server.use(bodyParser.urlencoded({ extended: false })) 21 | 22 | // Parse application/json 23 | server.use(bodyParser.json()) 24 | 25 | // Compress output stream 26 | server.use(compression()) 27 | } 28 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/modules/Env.js: -------------------------------------------------------------------------------- 1 | export const SET_ENV = "env/SET" 2 | 3 | /** 4 | * Selector for accessing the env values from inside the global state. 5 | * 6 | * @param {state} state Global Redux state. 7 | */ 8 | export function getEnv(state) { 9 | return state.env 10 | } 11 | 12 | /** 13 | * Action creator for setting the env values. 14 | * 15 | * @param {number} value New values to set for the env. 16 | */ 17 | export function setEnv(value) { 18 | return { type: SET_ENV, value } 19 | } 20 | 21 | const initialState = {} 22 | 23 | /** 24 | * Reducer for all env relevant action types. 25 | * 26 | * @param previousState Previous state object of this reducer. 27 | * @param {string} action Action to process. 28 | */ 29 | export function envReducer(previousState = initialState, action) { 30 | switch (action.type) { 31 | case SET_ENV: 32 | return { ...previousState, ...action.value } 33 | 34 | default: 35 | return previousState 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/edge-core/src/server.js: -------------------------------------------------------------------------------- 1 | // This file is just for exporting infrastructure to applications built upon this. 2 | 3 | // Polyfill for fetch() API in NodeJS based on code from 4 | // https://github.com/matthew-andrews/isomorphic-fetch/blob/master/fetch-npm-node.js 5 | if (!global.fetch) { 6 | var realFetch = require("node-fetch") 7 | global.fetch = function fetch(url, options) { 8 | const normalized = (/^\/\//).test(url) ? `https:${url}` : url 9 | return realFetch.call(this, normalized, options) 10 | } 11 | global.Response = realFetch.Response 12 | global.Headers = realFetch.Headers 13 | global.Request = realFetch.Request 14 | } 15 | 16 | export * from "./common" 17 | 18 | export { default as renderPage } from "./server/renderPage" 19 | export { default as renderApplication } from "./server/renderApplication" 20 | export { default as getLocaleData } from "./server/getLocaleData" 21 | export { default as prepareResponse } from "./server/prepareResponse" 22 | 23 | export * from "./server/debug" 24 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/binary.js: -------------------------------------------------------------------------------- 1 | import { resolve } from "path" 2 | import { createExpressServer } from "edge-express" 3 | import { loadConfig } from "edge-common" 4 | import "universal-dotenv" 5 | 6 | /* eslint-disable no-console, security/detect-non-literal-require */ 7 | async function main() { 8 | const { config } = await loadConfig() 9 | 10 | const clientStats = require(resolve(config.output.client, "stats.json")) 11 | const serverRender = require(resolve(config.output.server, "main.js")).default 12 | 13 | const server = createExpressServer({ 14 | staticConfig: { 15 | public: config.output.public, 16 | path: config.output.client 17 | }, 18 | localeConfig: config.locale, 19 | afterSecurity: [], 20 | beforeFallback: [ 21 | serverRender({ 22 | clientStats 23 | }) 24 | ] 25 | }) 26 | 27 | server.listen(process.env.SERVER_PORT, () => { 28 | console.log(`React Server Started @ Port ${process.env.SERVER_PORT}`) 29 | }) 30 | } 31 | 32 | process.nextTick(main) 33 | -------------------------------------------------------------------------------- /packages/edge-webpack/src/modules/Locales.js: -------------------------------------------------------------------------------- 1 | import webpack from "webpack" 2 | 3 | export const APP_LOCALES = process.env.APP_LOCALES ? 4 | process.env.APP_LOCALES.split(",") : 5 | [ process.env.APP_DEFAULT_LOCALE || "en-US" ] 6 | 7 | const LOCALES_MATCHER = new RegExp(`\\b(${APP_LOCALES.join("|")})\\b`) 8 | const LANGUAGES_MATCHER = new RegExp( 9 | `\\b(${APP_LOCALES.map((entry) => entry.split("-")[0]).join("|")})\\b` 10 | ) 11 | 12 | export default { 13 | plugins: [ 14 | // Fix context imports in Webpack to the supported locales 15 | // This entry is for lean-intl which uses locale specific data packs 16 | new webpack.ContextReplacementPlugin( 17 | /lean-intl[/\\]locale-data[/\\]js$/, 18 | LOCALES_MATCHER 19 | ), 20 | 21 | // Fix context imports in Webpack to the supported locales 22 | // This entry is for react-intl which uses language specific data packs 23 | new webpack.ContextReplacementPlugin( 24 | /react-intl[/\\]locale-data$/, 25 | LANGUAGES_MATCHER 26 | ) 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /packages/edge-style/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "edge-style", 3 | "version": "1.0.0-alpha.14", 4 | "description": "Styling Essentials for Edge. Part of the Edge Platform.", 5 | "main": "lib/index.js", 6 | "browser": "lib/index.js", 7 | "scripts": { 8 | "test": "echo", 9 | "build": "rimraf lib && prepublish", 10 | "prepack": "npm run build" 11 | }, 12 | "engines": { 13 | "node": ">=6.0.0", 14 | "yarn": ">=1.0.0", 15 | "npm": ">=4.0.0" 16 | }, 17 | "keywords": [ 18 | "css", 19 | "normalize", 20 | "sanitize", 21 | "reset", 22 | "opentype", 23 | "cssreset" 24 | ], 25 | "author": { 26 | "name": "Sebastian Software", 27 | "email": "s.werner@sebastian-software.de", 28 | "url": "https://www.sebastian-software.de" 29 | }, 30 | "files": [ 31 | "bin/", 32 | "docs/", 33 | "lib/" 34 | ], 35 | "license": "Apache-2.0", 36 | "dependencies": { 37 | "edge-postcss": "^1.0.0-alpha.14" 38 | }, 39 | "devDependencies": { 40 | "prepublish": "2.2.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/edge-core/src/common/State.test.js: -------------------------------------------------------------------------------- 1 | import { createReduxStore, createRootReducer } from "./State" 2 | 3 | test("Create Redux Store - Basic", () => { 4 | const reducers = {} 5 | const middlewares = [] 6 | const enhancers = [] 7 | 8 | expect(createReduxStore({ reducers, middlewares, enhancers })).toBeDefined() 9 | }) 10 | 11 | test("Create Redux Store - No Reducers", () => { 12 | const middlewares = [] 13 | const enhancers = [] 14 | 15 | expect(createReduxStore({ middlewares, enhancers })).toBeDefined() 16 | }) 17 | 18 | test("Create Redux Store - Empty Param", () => { 19 | expect(createReduxStore({})).toBeDefined() 20 | }) 21 | 22 | test("Create Redux Store - No Params", () => { 23 | expect(createReduxStore()).toBeDefined() 24 | }) 25 | 26 | test("Create Root Reducer", () => { 27 | expect(createRootReducer()).toBeDefined() 28 | }) 29 | 30 | test("Create Root Reducer with one reducer", () => { 31 | function dummy(prevState) { 32 | return prevState 33 | } 34 | 35 | expect(createRootReducer({ dummy })).toBeDefined() 36 | }) 37 | -------------------------------------------------------------------------------- /packages/edge-express/src/addFallbackHandler.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-magic-numbers, max-params */ 2 | export default function addFallbackHandler(server) { 3 | // Handle 404 errors. 4 | // Note: the react application middleware hands 404 paths, but it is good to 5 | // have this backup for paths not handled by the universal middleware. For 6 | // example you may bind a /api path to express. 7 | server.use((request, response, next) => { 8 | // eslint-disable-line no-unused-vars,max-len 9 | response.status(404).send("Sorry, that resource was not found.") 10 | }) 11 | 12 | // Handle all other errors (i.e. 500). 13 | // Note: You must provide specify all 4 parameters on this callback function 14 | // even if they aren't used, otherwise it won't be used. 15 | server.use((error, request, response, next) => { 16 | if (error) { 17 | /* eslint-disable no-console */ 18 | console.log(error) 19 | console.log(error.stack) 20 | } 21 | 22 | response.status(500).send("Sorry, an unexpected error occurred.") 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /packages/edge-common/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "edge-common", 3 | "description": "Edge common contains NodeJS based common utilities and helpers. Part of the Edge Platform.", 4 | "version": "1.0.0-alpha.10", 5 | "author": { 6 | "name": "Sebastian Software", 7 | "email": "s.werner@sebastian-software.de", 8 | "url": "https://www.sebastian-software.de" 9 | }, 10 | "main": "lib/index.cjs.js", 11 | "module": "lib/index.esm.js", 12 | "license": "Apache-2.0", 13 | "scripts": { 14 | "prepack": "npm run build", 15 | "build": "rimraf bin && rimraf lib && preppy", 16 | "test": "echo" 17 | }, 18 | "engines": { 19 | "node": ">=6.0.0", 20 | "yarn": ">=1.0.0", 21 | "npm": ">=4.0.0" 22 | }, 23 | "dependencies": { 24 | "app-root-dir": "^1.0.2", 25 | "chalk": "^2.4.1", 26 | "cosmiconfig": "^5.0.6", 27 | "jsome": "^2.5.0", 28 | "lodash": "^4.17.11", 29 | "node-notifier": "^5.2.1", 30 | "yn": "^2.0.0" 31 | }, 32 | "devDependencies": { 33 | "jest": "23.6.0", 34 | "preppy": "^4.4.0", 35 | "rimraf": "^2.6.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/edge-common/src/logging.js: -------------------------------------------------------------------------------- 1 | import chalk from "chalk" 2 | import notifier from "node-notifier" 3 | 4 | import { NAME, VERSION } from "./config" 5 | 6 | export function colorize(message, level = null) { 7 | switch (level) { 8 | case "warn": 9 | return chalk.yellow(message) 10 | 11 | case "error": 12 | return chalk.red(message) 13 | 14 | case "info": 15 | return chalk.green(message) 16 | 17 | default: 18 | return message 19 | } 20 | } 21 | 22 | export function notify(message, level = null) { 23 | notifier.notify({ 24 | title: `${NAME}-${VERSION}`, 25 | message 26 | }) 27 | 28 | /* eslint-disable no-console */ 29 | const consoleMessage = `${chalk.bold(NAME)}: ${colorize(message, level)}` 30 | switch (level) { 31 | case "warn": 32 | console.warn(consoleMessage) 33 | break 34 | 35 | case "error": 36 | console.error(consoleMessage) 37 | break 38 | 39 | case "info": 40 | console.log(consoleMessage) 41 | break 42 | 43 | default: 44 | console.log(consoleMessage) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/__snapshots__/assets.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Asset Size (Same Folder) 1`] = ` 4 | ".icon{ 5 | background-size:304px 85px 6 | }" 7 | `; 8 | 9 | exports[`Asset Size 1`] = ` 10 | ".icon{ 11 | background-size:304px 85px 12 | }" 13 | `; 14 | 15 | exports[`Asset URL (Same Folder) 1`] = ` 16 | ".icon{ 17 | background:url(./formula.png) 18 | }" 19 | `; 20 | 21 | exports[`Asset URL 1`] = ` 22 | ".icon{ 23 | background:url(../fixtures/formula.png) 24 | }" 25 | `; 26 | 27 | exports[`Import with Asset URL 1`] = ` 28 | ".image{ 29 | background:url(../fixtures/formula.png) 30 | }" 31 | `; 32 | 33 | exports[`Import with SVG URL 1`] = ` 34 | ".preloader{ 35 | background:url(../fixtures/other/logo.svg); 36 | border:1px solid #000 37 | }" 38 | `; 39 | 40 | exports[`Import with Webfonts 1`] = ` 41 | "@font-face{ 42 | src:url(../fixtures/font/SourceSansPro-Light.otf.woff2) format(\\"woff2\\"),url(../fixtures/font/SourceSansPro-Light.otf.woff) format(\\"woff\\"); 43 | font-family:Source Sans Pro; 44 | font-weight:300; 45 | font-style:normal 46 | }" 47 | `; 48 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | 4 | node_js: 5 | - 6 6 | - 8 7 | - 10 8 | 9 | matrix: 10 | fast_finish: true 11 | 12 | env: 13 | - CXX=g++-4.8 14 | 15 | addons: 16 | apt: 17 | sources: 18 | - ubuntu-toolchain-r-test 19 | packages: 20 | - g++-4.8 21 | 22 | install: 23 | yarn install --ignore-engines 24 | 25 | notifications: 26 | email: false 27 | slack: 28 | secure: bvUKXISN63vf7XnvOb9zI4KzgIb59HeDgZYrKSq63XpM3iX9Hm/j6njYPBFzYoVn6FTXwAdWmAG4EzbRIl0We7NNFivqCtFDdA3FMEaksLyb02OkPEfjyunChDpRoGdV5EzlS3YpsyWW/jYGSg22STBynWnbsDXEQZXu5SU9v58MJDV9w1kvbex4aLxlTt15M1cMjNmgHRhduHo8+dIS1Pb5MSl3hQUCgBppuD4rDFESC1MTAd047ZYF5PNbuAfGCpXcEyRVZMsD+A2kcO7VE3anaFvma7Qm9u9EN7yPuBKF2/mcZCx2Anmh8/XBK5/fAg4DeYq6pqjuQNiiq/SwZxvM+hEupwHXRZ13BkIzmSC6gnRlp9bhqU1HHotRwxm0gS8MrnEKCWhQi3f+ZWSIpIFXWQrdcqKNdw/zZvIn69UdAxqnzr3DTML7G8gKuXAky2Ibnx3bErUNA2wZP2vIYS9VUG85S1WQCbxTgxDfelDXoPHtZDWhqIM257g8WzLc51tJr4WSkIuXTxMBs9GBFdi9mrdvaAA3u4N+S9FK94VrpExnP9PEFQxo/KTY1wmhhi7hcvOgy03l/YoHl1EmVUEmamdbYM9nCXla6/KK5MHJ2HudiZwVMlXWinETC2MJ/ApHqy41cTit3hhy0EB7vc85Amr2QjOOeGTxCE89ThY= 29 | 30 | cache: 31 | yarn: true 32 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/__snapshots__/color.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`CSS4 HSL Colors 1`] = ` 4 | ".hsl{ 5 | color:red; 6 | border-color:rgba(0,255,255,.2) 7 | }" 8 | `; 9 | 10 | exports[`CSS4 HWB Colors 1`] = ` 11 | ".hwb{ 12 | color:rgba(128,255,0,.5) 13 | }" 14 | `; 15 | 16 | exports[`Color Function 1`] = ` 17 | ".yellow{ 18 | color:rgba(255,220,0,.9) 19 | }" 20 | `; 21 | 22 | exports[`Easing Gradients 1`] = ` 23 | ".demo{ 24 | background:linear-gradient(to bottom,#111 ,rgba(17,17,17,.90909) 12.34%,rgba(17,17,17,.80331) 23.32%,rgba(17,17,17,.6876) 33.19%,rgba(17,17,17,.56694) 42.17%,rgba(17,17,17,.44628) 50.5%,rgba(17,17,17,.33058) 58.42%,rgba(17,17,17,.22479) 66.16%,rgba(17,17,17,.13388) 73.95%,rgba(17,17,17,.06281) 82.03%,rgba(17,17,17,.01653) 90.64%,rgba(17,17,17,0)) 25 | }" 26 | `; 27 | 28 | exports[`Hex with Alpha 1`] = ` 29 | ".blue{ 30 | color:rgba(17,17,255,.8) 31 | }" 32 | `; 33 | 34 | exports[`Improved Color Palette 1`] = ` 35 | ".red{ 36 | color:#ff4136 37 | }" 38 | `; 39 | 40 | exports[`RGBA with Hex 1`] = ` 41 | ".red{ 42 | color:rgba(255,17,17,.8) 43 | }" 44 | `; 45 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/assets.js: -------------------------------------------------------------------------------- 1 | import { compile, compileSameFolder } from "./core" 2 | 3 | // ==================================================== 4 | // ================= URLS/ASSETS ====================== 5 | // ==================================================== 6 | 7 | test("Asset URL", () => 8 | compile(".icon { background: url('./fixtures/formula.png'); }")) 9 | 10 | test("Asset URL (Same Folder)", () => 11 | compileSameFolder(".icon { background: url('./formula.png'); }")) 12 | 13 | test("Asset Size", () => 14 | compile( 15 | ".icon { background-size: width('./fixtures/formula.png') height('./fixtures/formula.png'); }" 16 | )) 17 | 18 | test("Asset Size (Same Folder)", () => 19 | compileSameFolder( 20 | ".icon { background-size: width('./formula.png') height('./formula.png'); }" 21 | )) 22 | 23 | test("Import with Asset URL", () => 24 | compile("@import './fixtures/import-c.css';")) 25 | 26 | test("Import with SVG URL", () => 27 | compile("@import './fixtures/other/Home.css';")) 28 | 29 | test("Import with Webfonts", () => 30 | compile("@import './fixtures/font/SourceSansPro.css';")) 31 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/color.js: -------------------------------------------------------------------------------- 1 | import { compile } from "./core" 2 | 3 | // ==================================================== 4 | // ================== COLOR =========================== 5 | // ==================================================== 6 | 7 | test("Improved Color Palette", () => 8 | compile(".red { color: red; }") 9 | ) 10 | 11 | test("RGBA with Hex", () => 12 | compile(".red { color: rgba(#f11, 0.8); }") 13 | ) 14 | 15 | test("Hex with Alpha", () => 16 | compile(".blue { color: #11fc; }") 17 | ) 18 | 19 | test("Color Function", () => 20 | compile(".yellow { color: color(yellow a(90%)) }") 21 | ) 22 | 23 | test("Easing Gradients", () => 24 | compile(` 25 | .demo { 26 | background: linear-gradient( 27 | to bottom, 28 | black, 29 | cubic-bezier(0.48, 0.30, 0.64, 1.00), 30 | transparent 31 | ); 32 | }`)) 33 | 34 | test("CSS4 HSL Colors", () => 35 | compile(` 36 | .hsl { 37 | color: hsl(0 100% 50%); 38 | border-color: hsl(200grad 100% 50% / 20%); 39 | }`)) 40 | 41 | test("CSS4 HWB Colors", () => 42 | compile(` 43 | .hwb { 44 | color: hwb(90, 0%, 0%, 0.5); 45 | }`)) 46 | 47 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/fixes.js: -------------------------------------------------------------------------------- 1 | import { compile } from "./core" 2 | 3 | // ==================================================== 4 | // ================== FIXES =========================== 5 | // ==================================================== 6 | 7 | test("Font Variant", () => 8 | compile(` 9 | h2 { 10 | font-variant-caps: small-caps; 11 | } 12 | 13 | table { 14 | font-variant-numeric: lining-nums; 15 | }`)) 16 | 17 | test("Gradient Fix", () => 18 | compile(".elem { background-image: linear-gradient(green, transparent); }") 19 | ) 20 | 21 | test("Flexbox Fix", () => 22 | compile(".elem { flex: 1; }") 23 | ) 24 | 25 | test("Input Style Fixes", () => 26 | compile(` 27 | input[type="range"]::track { 28 | height: 3px; 29 | } 30 | 31 | input[type="range"]::thumb { 32 | width: 16px; 33 | height: 8px; 34 | } 35 | `) 36 | ) 37 | 38 | test("Autoprefixer", () => 39 | compile(":fullscreen a { display: flex }") 40 | ) 41 | 42 | test("Pseudoelements", () => 43 | compile("a::before { color: red; }") 44 | ) 45 | 46 | test("Selector matches()", () => 47 | compile("p:matches(:first-child, .special) { color: red; }") 48 | ) 49 | -------------------------------------------------------------------------------- /packages/edge-webpack/__tests__/project/project.test.js: -------------------------------------------------------------------------------- 1 | /* global __dirname */ 2 | 3 | import { join, resolve } from "path" 4 | import { readdirSync, readFileSync } from "fs" 5 | import rimraf from "rimraf" 6 | import webpack from "webpack" 7 | 8 | process.env.NODE_ENV = "production" 9 | 10 | /* eslint-disable-next-line import/no-commonjs */ 11 | const edge = require("../../src") 12 | 13 | const config = edge.full({ root: __dirname, quiet: true }) 14 | 15 | beforeEach((done) => { 16 | rimraf(resolve(__dirname, "dist"), done) 17 | }) 18 | 19 | test("Executes correctly", (done) => { 20 | const compiler = webpack(config) 21 | compiler.run((err, stats) => { 22 | if (err || stats.hasErrors()) { 23 | // Handle errors here 24 | throw new Error(err) 25 | } 26 | 27 | const dist = join(__dirname, "dist") 28 | 29 | expect(readdirSync(dist)).toMatchSnapshot("dirlist") 30 | expect(readFileSync(`${dist}/index.html`, "utf-8")).toMatchSnapshot("htmlfile") 31 | expect(readFileSync(`${dist}/file-1UZ6YEQK.svg`, "utf-8")).toMatchSnapshot("logofile") 32 | }) 33 | 34 | compiler.hooks.done.tap("Test", () => setTimeout(done)) 35 | }) 36 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/layout.js: -------------------------------------------------------------------------------- 1 | import { compile } from "./core" 2 | 3 | // ==================================================== 4 | // ================== LAYOUT ========================== 5 | // ==================================================== 6 | 7 | test("Lost Grid", () => 8 | compile(".grid { lost-column: 3/12 }") 9 | ) 10 | 11 | test("Grid KISS", () => 12 | compile(` 13 | .gridTest { 14 | grid-kiss: 15 | "+-------------------------------+ " 16 | "| header ↑ | 120px" 17 | "+-------------------------------+ " 18 | " " 19 | "+-- 30% ---+ +--- auto --------+ " 20 | "| .sidebar | | main | auto " 21 | "+----------+ +-----------------+ " 22 | " " 23 | "+-------------------------------+ " 24 | "| ↓ | 60px " 25 | "| → footer ← | " 26 | "+-------------------------------+ " 27 | } 28 | `) 29 | ) 30 | 31 | test("Flow Root", () => 32 | compile(".selector { display: flow-root; }") 33 | ) 34 | -------------------------------------------------------------------------------- /packages/edge-webpack/src/modules/Optimization.js: -------------------------------------------------------------------------------- 1 | import UglifyJsPlugin from "uglifyjs-webpack-plugin" 2 | import OptimizeCSSAssetsPlugin from "optimize-css-assets-webpack-plugin" 3 | 4 | import { IS_PRODUCTION, ENABLE_SOURCE_MAPS } from "../config" 5 | 6 | export default { 7 | optimization: { 8 | // Docs: https://webpack.js.org/plugins/split-chunks-plugin/ 9 | splitChunks: { 10 | // There are some issues with HtmlWebpackPlugin and the automatic vendor chunk right now. 11 | // chunks: "all", 12 | // Since the chunk name includes all origin chunk names it’s recommended for production builds 13 | // with long term caching to NOT include [name] in the filenames, or switch off name generation 14 | // Via: https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366 15 | // name: false 16 | }, 17 | minimizer: IS_PRODUCTION ? 18 | [ 19 | new UglifyJsPlugin({ 20 | cache: true, 21 | parallel: true, 22 | sourceMap: ENABLE_SOURCE_MAPS 23 | }), 24 | new OptimizeCSSAssetsPlugin({}) 25 | ] : 26 | [] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/State.js: -------------------------------------------------------------------------------- 1 | import { counterReducer } from "./modules/Counter" 2 | import { envReducer } from "./modules/Env" 3 | 4 | export default { 5 | /** 6 | * Returns configuration objects for different areas of the Edge 7 | * powered application infrastructure. 8 | * 9 | * @param state {Object} Current application state. 10 | * @param topic {String} Any of "apollo", ... 11 | */ 12 | getConfig(state, topic) { 13 | if (topic === "apollo") { 14 | return { 15 | uri: state.env.APOLLO_URL 16 | 17 | // headers: {}, 18 | // batchRequests: false, 19 | // trustNetwork: true, 20 | // queryDeduplication: true 21 | } 22 | } 23 | 24 | return null 25 | }, 26 | 27 | /** 28 | * Return list of Redux store enhancers to use. 29 | */ 30 | getEnhancers() { 31 | return [] 32 | }, 33 | 34 | /** 35 | * Create mapping of reducers to use for the Redux store. 36 | */ 37 | getReducers() { 38 | return { 39 | counter: counterReducer, 40 | env: envReducer 41 | } 42 | }, 43 | 44 | /** 45 | * Create list of Redux middleware to use. 46 | */ 47 | getMiddlewares() { 48 | return [] 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/edge-webpack/src/modules/Experience.js: -------------------------------------------------------------------------------- 1 | import webpack from "webpack" 2 | import ErrorOverlayPlugin from "error-overlay-webpack-plugin" 3 | import FriendlyPlugin from "friendly-errors-webpack-plugin" 4 | 5 | import { IS_DEVELOPMENT, IS_PRODUCTION } from "../config" 6 | 7 | const stats = "minimal" 8 | const logLevel = "warn" 9 | 10 | export default { 11 | stats, 12 | 13 | // The new development web server of Webpack 14 | // See also: https://github.com/webpack-contrib/webpack-serve 15 | serve: { 16 | devMiddleware: { 17 | logLevel, 18 | stats 19 | }, 20 | 21 | hotClient: { 22 | logLevel 23 | } 24 | }, 25 | 26 | // The legacy development web server of Webpack 27 | // See also: https://webpack.js.org/configuration/dev-server/ 28 | devServer: { 29 | stats, 30 | clientLogLevel: logLevel 31 | }, 32 | 33 | plugins: [ 34 | IS_DEVELOPMENT || IS_PRODUCTION ? 35 | new FriendlyPlugin({ 36 | clearConsole: false 37 | }) : 38 | null, 39 | 40 | IS_DEVELOPMENT ? new ErrorOverlayPlugin() : null, 41 | 42 | // Does not work well with HMR and Dev Server 43 | IS_PRODUCTION ? new webpack.ProgressPlugin() : null 44 | ].filter(Boolean) 45 | } 46 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Use Unix line breaks for all text by default 2 | # Unfortunately this requires to define binary types as well as 3 | # this applies to all files - not just text. 4 | * text eol=lf 5 | 6 | # GRAPHICS 7 | *.ai binary 8 | *.bmp binary 9 | *.eps binary 10 | *.gif binary 11 | *.ico binary 12 | *.jng binary 13 | *.jp2 binary 14 | *.jpg binary 15 | *.jpeg binary 16 | *.jpx binary 17 | *.jxr binary 18 | *.pdf binary 19 | *.png binary 20 | *.psb binary 21 | *.psd binary 22 | *.svg text 23 | *.svgz binary 24 | *.tif binary 25 | *.tiff binary 26 | *.wbmp binary 27 | *.webp binary 28 | 29 | # AUDIO 30 | *.kar binary 31 | *.m4a binary 32 | *.mid binary 33 | *.midi binary 34 | *.mp3 binary 35 | *.ogg binary 36 | *.ra binary 37 | 38 | # VIDEO 39 | *.3gpp binary 40 | *.3gp binary 41 | *.as binary 42 | *.asf binary 43 | *.asx binary 44 | *.fla binary 45 | *.flv binary 46 | *.m4v binary 47 | *.mng binary 48 | *.mov binary 49 | *.mp4 binary 50 | *.mpeg binary 51 | *.mpg binary 52 | *.swc binary 53 | *.swf binary 54 | *.webm binary 55 | 56 | # ARCHIVES 57 | *.7z binary 58 | *.gz binary 59 | *.rar binary 60 | *.tar binary 61 | *.zip binary 62 | 63 | # FONTS 64 | *.ttf binary 65 | *.eot binary 66 | *.otf binary 67 | *.woff binary 68 | *.woff2 binary 69 | -------------------------------------------------------------------------------- /packages/edge-storybook/src/webpack.config.js: -------------------------------------------------------------------------------- 1 | import { core } from "edge-webpack" 2 | 3 | /* eslint-disable import/no-commonjs */ 4 | // Export a function. Accept the base config as the only param. 5 | module.exports = (storybookBaseConfig, configType) => { 6 | // configType has a value of 'DEVELOPMENT' or 'PRODUCTION' 7 | // You can change the configuration based on that. 8 | // 'PRODUCTION' is used when building the static version of storybook. 9 | 10 | // Replace Storybooks Babel Loader with local one. 11 | // This enables Babel v7 support. 12 | storybookBaseConfig.module.rules.forEach((rule) => { 13 | if (/babel/.exec(rule.loader)) { 14 | rule.loader = require.resolve("babel-loader") 15 | } 16 | }) 17 | 18 | const options = {} 19 | const config = core(options) 20 | 21 | storybookBaseConfig.node = config.node 22 | 23 | // Do not flood the console with thousands of messages 24 | storybookBaseConfig.stats = config.stats 25 | storybookBaseConfig.devServer = config.devServer 26 | 27 | // Append our loaders to the file specific rules 28 | storybookBaseConfig.module.rules.push(...config.module.rules) 29 | 30 | // As well as our set of plugins 31 | storybookBaseConfig.plugins.push(...config.plugins) 32 | 33 | return storybookBaseConfig 34 | } 35 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/server/index.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { fetchData, prepareResponse, renderApplication } from "edge-core" 3 | import { getLoadableState } from 'loadable-components/server' 4 | 5 | import Application from "../Application" 6 | import State from "../State" 7 | 8 | // eslint-disable-next-line no-console 9 | console.log(`[APP] Build: ${process.env.NODE_ENV}-${process.env.BUILD_TARGET}`) 10 | 11 | /* eslint-disable no-console, max-statements */ 12 | export default ({ clientStats }) => async (request, response) => { 13 | // Response Preparation: 14 | // This step parses some client information like language and user agent. 15 | const parsed = prepareResponse(request) 16 | 17 | // Fetch Data: 18 | // Now we are ready to fetch required data by waiting for async requests. 19 | try { 20 | await fetchData(Application) 21 | } catch (error) { 22 | console.error("Unable to fetch data:", error) 23 | } 24 | 25 | // Render Application: 26 | // When all required data is available we can safely render the result. 27 | try { 28 | renderApplication({ 29 | Application, 30 | clientStats, 31 | request, 32 | response 33 | }) 34 | } catch(error) { 35 | console.error("Unable to render application:", error) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/edge-webpack/__tests__/project/__snapshots__/project.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Executes correctly: dirlist 1`] = ` 4 | Array [ 5 | "file-1UZ6YEQK.svg", 6 | "index.fxFXOoXR.js", 7 | "index.html", 8 | ] 9 | `; 10 | 11 | exports[`Executes correctly: htmlfile 1`] = ` 12 | " 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | " 21 | `; 22 | 23 | exports[`Executes correctly: logofile 1`] = `""`; 24 | -------------------------------------------------------------------------------- /packages/edge-express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "edge-express", 3 | "description": "Edge Express is a centralized Express-based HTTP server with sophisticated built-in security.", 4 | "version": "1.0.0-alpha.10", 5 | "author": { 6 | "name": "Sebastian Software", 7 | "email": "s.werner@sebastian-software.de", 8 | "url": "https://www.sebastian-software.de" 9 | }, 10 | "main": "lib/index.cjs.js", 11 | "module": "lib/index.esm.js", 12 | "license": "Apache-2.0", 13 | "scripts": { 14 | "prepack": "npm run build", 15 | "build": "rimraf lib && prepublish", 16 | "test": "echo" 17 | }, 18 | "files": [ 19 | "bin/", 20 | "docs/", 21 | "lib/" 22 | ], 23 | "engines": { 24 | "node": ">=6.0.0", 25 | "yarn": ">=1.0.0", 26 | "npm": ">=4.0.0" 27 | }, 28 | "dependencies": { 29 | "app-root-dir": "^1.0.2", 30 | "body-parser": "^1.18.3", 31 | "chalk": "^2.4.1", 32 | "compression": "^1.7.3", 33 | "cookie-parser": "^1.4.3", 34 | "cosmiconfig": "^5.0.6", 35 | "express": "^4.16.3", 36 | "express-locale": "^1.0.5", 37 | "helmet": "^3.13.0", 38 | "hpp": "^0.2.2", 39 | "jsome": "^2.5.0", 40 | "pretty-error": "^2.1.1", 41 | "uuid": "^3.3.2" 42 | }, 43 | "devDependencies": { 44 | "jest": "23.6.0", 45 | "prepublish": "2.2.0", 46 | "rimraf": "*" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/edge-core/src/common/createKernel.js: -------------------------------------------------------------------------------- 1 | import { createApolloClient } from "./ApolloClient" 2 | import { createReduxStore } from "./State" 3 | import getBrowserLocale from "../client/getBrowserLocale" 4 | 5 | /* global window */ 6 | const defaultState = process.env.TARGET === "web" ? window.APP_STATE : null 7 | 8 | export default function createKernel(State, { state = defaultState, edge, request, supportedLocales } = {}) { 9 | // Use given edge instance when not already defined on state 10 | if (process.env.TARGET === "node" && edge != null) { 11 | if (!state.edge) { 12 | state.edge = edge 13 | } 14 | } 15 | 16 | if (process.env.TARGET === "web" && state.edge.intl == null) { 17 | console.warn("Fallback to client side locale information!") 18 | state.edge.intl = getBrowserLocale(supportedLocales || [ "en-US" ]) 19 | } 20 | 21 | const apolloConfig = State.getConfig(state, "apollo") 22 | let apollo = null 23 | if (apolloConfig) { 24 | apollo = createApolloClient(apolloConfig) 25 | } 26 | 27 | const store = createReduxStore({ 28 | reducers: State.getReducers(), 29 | enhancers: State.getEnhancers(), 30 | middlewares: State.getMiddlewares(), 31 | state 32 | }) 33 | 34 | const intl = state.edge.intl 35 | 36 | // Kernel "Instance" 37 | return { 38 | intl, 39 | apollo, 40 | store 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/edge-core/src/common/wrapApplication.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import { Provider } from "react-redux" 4 | import { ApolloProvider } from "react-apollo" 5 | import { IntlProvider } from "react-intl" 6 | 7 | /** 8 | * Wraps the application class with different providers for offering the 9 | * following features: 10 | * 11 | * - Apollo GraphQL 12 | * - Redux 13 | * - React Intl 14 | * 15 | * This might be extended with new features during development. 16 | * 17 | * @param {React.Component} Application The React root application component. 18 | * @param {Kernel} kernel Kernel instance which holds the data oriented runtime state. 19 | * @returns {React.Component} Returns the wrapped application component. 20 | */ 21 | export default function wrapApplication(Application, kernel) { 22 | let Wrapped = Application 23 | 24 | if (kernel.apollo) { 25 | Wrapped = ( 26 | 27 | {Wrapped} 28 | 29 | ) 30 | } 31 | 32 | if (kernel.store) { 33 | Wrapped = ( 34 | 35 | {Wrapped} 36 | 37 | ) 38 | } 39 | 40 | if (kernel.intl) { 41 | Wrapped = ( 42 | 43 | {Wrapped} 44 | 45 | ) 46 | } 47 | 48 | return Wrapped 49 | } 50 | -------------------------------------------------------------------------------- /packages/edge-core/src/server/renderApplication.js: -------------------------------------------------------------------------------- 1 | import { renderToString } from "react-dom/server" 2 | import flushChunks from "webpack-flush-chunks" 3 | 4 | import renderPage from "./renderPage" 5 | 6 | /* eslint-disable max-params, no-console */ 7 | export default function renderApplication({ 8 | Application, 9 | clientStats, 10 | kernel, 11 | request, 12 | response 13 | }) { 14 | console.log("[EDGE] Rendering application...") 15 | let html = "" 16 | try { 17 | html = renderToString(Application) 18 | } catch (err) { 19 | console.error("Unable to render server side React:", err) 20 | } 21 | 22 | const { js, styles } = flushChunks(clientStats) 23 | 24 | // TODO: Support SRI integrity checksums as added by SRI Webpack Plugin 25 | // https://www.npmjs.com/package/webpack-subresource-integrity#without-htmlwebpackplugin 26 | 27 | console.log("HTML:",html) 28 | 29 | // Render full HTML page using external helper 30 | console.log("Rendering Page...") 31 | const renderedPage = renderPage({ 32 | html, 33 | styles: styles.toString(), 34 | scripts: js.toString() 35 | }) 36 | 37 | // Make sure that the actual dynamically rendered page is never being cached 38 | response.setHeader("Cache-Control", "no-cache") 39 | 40 | // Send actual content 41 | console.log("[EDGE] Sending Page...") 42 | return response.status(200).send(renderedPage) 43 | } 44 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/core.js: -------------------------------------------------------------------------------- 1 | import postcss from "postcss" 2 | import loadConfig from "postcss-load-config" 3 | 4 | let plugins = null 5 | let options = null 6 | 7 | beforeAll(async function() { 8 | const config = await loadConfig({ map: false }) 9 | 10 | plugins = config.plugins 11 | options = config.options 12 | }) 13 | 14 | // We add some super basic formatting to our CSS to make snapshots better readable 15 | // and inspectable in case of any regressions later on. 16 | function format(cssString) { 17 | return cssString 18 | .replace(/;/g, ";\n") 19 | .replace(/}/g, "\n}\n\n") 20 | .replace(/{/g, "{\n") 21 | .replace(/}\n\n\n}/g, "}\n}\n\n") 22 | .replace(/\n\n\n\n/g, "\n\n") 23 | .trim() 24 | } 25 | 26 | export async function compile(input) { 27 | const allOptions = { 28 | ...options, 29 | from: "__tests__/main.css", 30 | to: "__tests__/dist/main.css" 31 | } 32 | 33 | const result = await postcss(plugins) 34 | .process(input, allOptions) 35 | 36 | expect(format(result.css)).toMatchSnapshot() 37 | } 38 | 39 | export async function compileSameFolder(input) { 40 | const allOptions = { 41 | ...options, 42 | from: "__tests__/fixtures/main.css", 43 | to: "__tests__/fixtures/main.out.css" 44 | } 45 | 46 | const result = await postcss(plugins) 47 | .process(input, allOptions) 48 | 49 | expect(format(result.css)).toMatchSnapshot() 50 | } 51 | -------------------------------------------------------------------------------- /packages/edge-style/src/Intl.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable selector-max-type, comment-empty-line-before */ 2 | 3 | /** 4 | * According to Wikipedia: 5 | * http://en.wikipedia.org/wiki/Non-English_usage_of_quotation_marks 6 | */ 7 | 8 | :lang(cy) q { 9 | quotes: "\2018" "\2019" "\201C" "\201D" "\2018" "\2019" "\201C" "\201D"; 10 | /* ‘ ’ “ ” ‘ ’ “ ” */ 11 | } 12 | 13 | :lang(af) q, 14 | :lang(zh) q, 15 | :lang(en) q, 16 | :lang(en-us) q, 17 | :lang(eo) q, 18 | :lang(id) q, 19 | :lang(ga) q, 20 | :lang(ko) q, 21 | :lang(pt-br) q, 22 | :lang(th) q, 23 | :lang(tr) q { 24 | quotes: "\201C" "\201D" "\2018" "\2019" "\201C" "\201D" "\2018" "\2019"; 25 | /* “ ” ‘ ’ “ ” ‘ ’ */ 26 | } 27 | 28 | :lang(sq) q, 29 | :lang(bs) q { 30 | quotes: "\201E" "\201C" "\2018" "\2019" "\201E" "\201C" "\2018" "\2019"; 31 | /* „ “ ‘ ’ „ “ ‘ ’ */ 32 | } 33 | 34 | :lang(ar) q { 35 | quotes: "\201D" "\201C"; 36 | /* ” “ */ 37 | } 38 | 39 | :lang(cs) q, 40 | :lang(de) q, 41 | :lang(sk) q, 42 | :lang(sl) q, 43 | :lang(sb) q, 44 | :lang(is) q { 45 | quotes: "\201E" "\201C" "\201A" "\2018" "\201E" "\201C" "\201A" "\2018"; 46 | /* „ “ ‚ ‘ „ “ ‚ ‘ */ 47 | } 48 | 49 | q::before { 50 | content: open-quote; 51 | } 52 | 53 | q::after { 54 | content: close-quote; 55 | } 56 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/__snapshots__/effects.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Easings 1`] = ` 4 | ".snake{ 5 | transition:all 600ms cubic-bezier(.47,0,.745,.715) 6 | }" 7 | `; 8 | 9 | exports[`Magic Animations 1`] = ` 10 | "@keyframes magic{ 11 | 0%{ 12 | opacity:1; 13 | transform-origin:100% 200%; 14 | transform:scale(1,1) rotate(0deg) 15 | } 16 | 17 | to{ 18 | opacity:0; 19 | transform-origin:200% 500%; 20 | transform:scale(0,0) rotate(270deg) 21 | } 22 | } 23 | 24 | .animation{ 25 | animation-name:magic 26 | }" 27 | `; 28 | 29 | exports[`Pleeease Filters 1`] = ` 30 | ".box{ 31 | filter:url('data:image/svg+xml; 32 | charset=utf-8,#filter'); 33 | -webkit-filter:drop-shadow(16px 16px 20px #00f); 34 | filter:drop-shadow(16px 16px 20px #00f) 35 | }" 36 | `; 37 | 38 | exports[`Transform Shortcut 1`] = ` 39 | ".transform{ 40 | transform:scale3d(2,1,1) translate3d(10px,20px,0) 41 | }" 42 | `; 43 | 44 | exports[`Will Change Compat 1`] = ` 45 | ".scaled{ 46 | -webkit-backface-visibility:hidden; 47 | backface-visibility:hidden; 48 | will-change:width 49 | }" 50 | `; 51 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/components/htmlhead/HtmlHead.js: -------------------------------------------------------------------------------- 1 | import Helmet from "react-helmet" 2 | import React from "react" 3 | 4 | /* eslint-disable import/no-webpack-loader-syntax */ 5 | 6 | import AppleTouchIcon from "./apple-touch-icon.png" 7 | import ClassicFavicon from "./favicon.ico" 8 | import FavIcon16 from "./favicon-16x16.png" 9 | import FavIcon32 from "./favicon-32x32.png" 10 | import Manifest from "./manifest.webmanifest" 11 | import SafariPinned from "./safari-pinned-tab.svg" 12 | 13 | export default function HtmlHead() { 14 | return ( 15 | /* eslint-disable no-inline-comments */ 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | {/* http://httpwg.org/http-extensions/client-hints.html */} 33 | 34 | 35 | 39 | 40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /packages/edge-core/readme.md: -------------------------------------------------------------------------------- 1 | # Edge Core
[![Sponsored by][sponsor-img]][sponsor] [![Version][npm-version-img]][npm] [![Downloads][npm-downloads-img]][npm] [![Build Status Unix][travis-img]][travis] [![Build Status Windows][appveyor-img]][appveyor] [![Dependencies][deps-img]][deps] 2 | 3 | [sponsor-img]: https://img.shields.io/badge/Sponsored%20by-Sebastian%20Software-692446.svg 4 | [sponsor]: https://www.sebastian-software.de 5 | [deps]: https://david-dm.org/sebastian-software/edge-core 6 | [deps-img]: https://david-dm.org/sebastian-software/edge-core.svg 7 | [npm]: https://www.npmjs.com/package/edge-core 8 | [npm-downloads-img]: https://img.shields.io/npm/dm/edge-core.svg 9 | [npm-version-img]: https://img.shields.io/npm/v/edge-core.svg 10 | [travis-img]: https://img.shields.io/travis/sebastian-software/edge-core/master.svg?branch=master&label=unix%20build 11 | [appveyor-img]: https://img.shields.io/appveyor/ci/swernerx/edge-core/master.svg?label=windows%20build 12 | [travis]: https://travis-ci.org/sebastian-software/edge-core 13 | [appveyor]: https://ci.appveyor.com/project/swernerx/edge-core/branch/master 14 | 15 | > The Edge Platform helps you focus on business logic rather than dealing with massive tooling, common patterns, complex configurations. 16 | 17 | 18 | 19 | ## License 20 | 21 | [Apache License Version 2.0, January 2004](license) 22 | 23 | 24 | ## Copyright 25 | 26 | Logo of Sebastian Software GmbH, Mainz, Germany 27 | 28 | Copyright 2017-2018
[Sebastian Software GmbH](http://www.sebastian-software.de) 29 | -------------------------------------------------------------------------------- /packages/edge-core/src/client/getBrowserLocale.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns the browser locale settings based on available locales and browser settings. 3 | * 4 | * @param {Array} supportedLocales List of supported locales by the application. 5 | */ 6 | export default function getBrowserLocale(supportedLocales) { 7 | return process.env.TARGET === "web" ? _getBrowserLocale(supportedLocales) : null 8 | } 9 | 10 | function _getBrowserLocale(supportedLocales) { 11 | const supported = new Set(supportedLocales) 12 | const available = new Set() 13 | 14 | // Modern standard: Support by modern Chrome, Safari and Firefox 15 | const languages = navigator.languages 16 | if (languages) { 17 | for (const lang of languages) { 18 | if (supported.has(lang)) { 19 | available.add(lang) 20 | } 21 | } 22 | } 23 | 24 | // Microsoft standard 25 | const userLanguage = navigator.userLanguage 26 | if (userLanguage) { 27 | const wellFormedUserLanguage = (() => { 28 | const splitted = userLanguage.split("-") 29 | return `${splitted[0]}-${splitted[1].toUpperCase()}` 30 | })() 31 | 32 | if (supported.has(wellFormedUserLanguage)) { 33 | available.add(wellFormedUserLanguage) 34 | } 35 | } 36 | 37 | // Legacy API 38 | const language = navigator.language 39 | if (language && supported.has(language)) { 40 | available.add(language) 41 | } 42 | 43 | // Return only the first match 44 | const first = Array.from(available.values())[0] 45 | return first ? { 46 | locale: first, 47 | language: first.split("-")[0], 48 | region: first.split("-")[1] || first.split("-")[0].toUpperCase() 49 | } : null 50 | } 51 | -------------------------------------------------------------------------------- /packages/jest-preset-edge/jest-preset.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-commonjs, filenames/match-regex */ 2 | module.exports = { 3 | transform: { 4 | "^.+\\.(js|jsx|mjs)$": require.resolve("./transform/babel.js"), 5 | "^.+\\.(css|sss|scss|sass)$": require.resolve("./transform/css.js"), 6 | "^.+\\.(graphql|gql)$": require.resolve("./transform/graphql.js"), 7 | "^(?!.*\\.(js|jsx|mjs|css|sss|scss|sass|json|graphql|gql)$)": require.resolve( 8 | "./transform/file.js" 9 | ) 10 | }, 11 | 12 | transformIgnorePatterns: [ 13 | "node_modules/.+\\.(js|jsx|mjs)$", 14 | "^.+\\.module\\.css$" 15 | ], 16 | 17 | moduleNameMapper: { 18 | "^.+\\.module\\.css$": "identity-obj-proxy" 19 | }, 20 | 21 | setupFiles: [ 22 | require.resolve("./setup.js") 23 | ], 24 | 25 | collectCoverageFrom: [ 26 | "src/**/*.js" 27 | ], 28 | 29 | coverageDirectory: "docs/coverage", 30 | 31 | coveragePathIgnorePatterns: [ 32 | // NPM packages 33 | "/node_modules/", 34 | 35 | // Publish Export Entries 36 | "src/index.js", 37 | "src/client.js", 38 | "src/server.js", 39 | "src/binary.js", 40 | "src/dev.js", 41 | 42 | // Webpack Entry Points 43 | "/client/", 44 | "/server/", 45 | 46 | // Application Glue Code 47 | "src/App.js", 48 | "src/Main.js", 49 | "src/State.js", 50 | "src/Init.js", 51 | 52 | // Views aka Route Entry Points 53 | "/views/", 54 | 55 | // Storybook Stories 56 | ".story.js", 57 | 58 | // Jest Tests 59 | ".test.js" 60 | ], 61 | 62 | coverageReporters: [ 63 | "lcov", 64 | "json-summary", 65 | "json", 66 | "text" 67 | ] 68 | } 69 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/fonts/source-sans-pro/SourceSansPro.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | src: 3 | url("./SourceSansPro-ExtraLight.otf.woff2") format("woff2"), 4 | url("./SourceSansPro-ExtraLight.otf.woff") format("woff"); 5 | 6 | font-family: Source Sans Pro; 7 | font-weight: 200; 8 | font-style: normal; 9 | } 10 | 11 | @font-face { 12 | src: 13 | url("./SourceSansPro-Light.otf.woff2") format("woff2"), 14 | url("./SourceSansPro-Light.otf.woff") format("woff"); 15 | 16 | font-family: Source Sans Pro; 17 | font-weight: 300; 18 | font-style: normal; 19 | } 20 | 21 | @font-face { 22 | src: 23 | url("./SourceSansPro-Regular.otf.woff2") format("woff2"), 24 | url("./SourceSansPro-Regular.otf.woff") format("woff"); 25 | 26 | font-family: Source Sans Pro; 27 | 28 | /* 400 = normal by CSS standards */ 29 | font-weight: 400; 30 | font-style: normal; 31 | } 32 | 33 | @font-face { 34 | src: 35 | url("./SourceSansPro-Semibold.otf.woff2") format("woff2"), 36 | url("./SourceSansPro-Semibold.otf.woff") format("woff"); 37 | 38 | font-family: Source Sans Pro; 39 | font-weight: 500; 40 | font-style: normal; 41 | } 42 | 43 | @font-face { 44 | src: 45 | url("./SourceSansPro-Bold.otf.woff2") format("woff2"), 46 | url("./SourceSansPro-Bold.otf.woff") format("woff"); 47 | 48 | font-family: Source Sans Pro; 49 | 50 | /* 700 = bold by CSS standards */ 51 | font-weight: 700; 52 | font-style: normal; 53 | } 54 | 55 | @font-face { 56 | src: 57 | url("./SourceSansPro-Black.otf.woff2") format("woff2"), 58 | url("./SourceSansPro-Black.otf.woff") format("woff"); 59 | 60 | font-family: Source Sans Pro; 61 | font-weight: 800; 62 | font-style: normal; 63 | } 64 | -------------------------------------------------------------------------------- /packages/edge-storybook/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "edge-storybook", 3 | "version": "1.0.0-alpha.32", 4 | "description": "Central configuration for Storybook. For usage in component collections and applications.", 5 | "main": "lib/config.js", 6 | "scripts": { 7 | "test": "echo", 8 | "build": "rimraf lib && cpy src/babelrc --rename=.babelrc lib/ && babel --out-dir lib src", 9 | "prepack": "npm run build" 10 | }, 11 | "files": [ 12 | "lib/" 13 | ], 14 | "keywords": [], 15 | "author": { 16 | "name": "Sebastian Software", 17 | "email": "s.werner@sebastian-software.de", 18 | "url": "https://www.sebastian-software.de" 19 | }, 20 | "license": "Apache-2.0", 21 | "dependencies": { 22 | "@storybook/addon-actions": "^4.0.0-alpha.4", 23 | "@storybook/addon-jest": "^3.4.11", 24 | "@storybook/addon-links": "^4.0.0-alpha.4", 25 | "@storybook/addon-storyshots": "^4.0.0-alpha.4", 26 | "@storybook/addon-storysource": "^3.4.11", 27 | "@storybook/addon-viewport": "^4.0.0-alpha.4", 28 | "@storybook/react": "^4.0.0-alpha.4", 29 | "edge-style": "^1.0.0-alpha.14", 30 | "edge-webpack": "^0.0.2-alpha.17", 31 | "global": "^4.3.2", 32 | "js-cookie": "^2.2.0", 33 | "react-intl": "^2.6.0", 34 | "universal-dotenv": "^1.9.1" 35 | }, 36 | "peerDependencies": { 37 | "react": ">=16.4.0", 38 | "react-dom": ">=16.4.0" 39 | }, 40 | "devDependencies": { 41 | "@babel/cli": "^7.1.0", 42 | "@babel/core": "^7.1.0", 43 | "babel-core": "^7.0.0-bridge.0", 44 | "babel-loader": "^8.0.2", 45 | "babel-preset-edge": "^4.13.1", 46 | "cpy-cli": "^2.0.0", 47 | "react": "^16.5.2", 48 | "react-dom": "^16.5.2", 49 | "rimraf": "^2.6.2" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/edge-useragent/test/parser.qa.js: -------------------------------------------------------------------------------- 1 | import { parse } from "../src" 2 | import should from "should" 3 | import yaml from "yamlparser" 4 | import fs from "fs" 5 | 6 | // run over the testcases, some might fail, some might not. This is just qu 7 | // test to see if we can parse without errors, and with a reasonable amount 8 | // of errors. 9 | ;["static.custom.yaml", "firefoxes.yaml", "pgts.yaml"].forEach(filename => { 10 | let testcases = fs 11 | .readFileSync(`${__dirname}/fixtures/${filename}`) 12 | .toString() 13 | const parsedyaml = yaml.eval(testcases) 14 | 15 | testcases = parsedyaml.test_cases 16 | testcases.forEach(test => { 17 | // we are unable to parse these tests atm because invalid JSON is used to 18 | // store the useragents 19 | if (typeof test.user_agent_string !== "string") return 20 | 21 | // these tests suck as the test files are broken, enable this to have about 22 | // 40 more failing tests 23 | if (test.family.match(/googlebot|avant/i)) return 24 | 25 | // attempt to parse the shizzle js based stuff 26 | let js_ua 27 | if (test.js_ua) { 28 | js_ua = Function(`return ${test.js_ua}`)().js_user_agent_string 29 | } 30 | 31 | exports[`${filename}: ${test.user_agent_string}`] = () => { 32 | const agent = parse(test.user_agent_string, js_ua) 33 | 34 | agent.family.should.equal(test.family) 35 | // we need to test if v1 is a string, because the yamlparser transforms 36 | // empty v1: statements to {} 37 | agent.major.should.equal(typeof test.major == "string" ? test.major : "0") 38 | agent.minor.should.equal(typeof test.minor == "string" ? test.minor : "0") 39 | agent.patch.should.equal(typeof test.patch == "string" ? test.patch : "0") 40 | } 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /packages/edge-core/src/common/ApolloClient.js: -------------------------------------------------------------------------------- 1 | import { ApolloClient } from "apollo-client" 2 | import { createHttpLink } from "apollo-link-http" 3 | import { InMemoryCache } from "apollo-cache-inmemory" 4 | import { setContext } from "apollo-link-context" 5 | 6 | if (process.env.NODE_ENV === "test") { 7 | global.fetch = require("jest-fetch-mock") 8 | } 9 | 10 | export function createApolloClient(config = {}) { 11 | const { 12 | uri = null, 13 | getAuthHeader = null, 14 | trustNetwork = true, 15 | clientOptions = {}, 16 | linkOptions = {} 17 | } = config 18 | 19 | if (uri == null) { 20 | return null 21 | } 22 | 23 | const finalLinkOptions = { 24 | // Based on user given options 25 | ...linkOptions, 26 | 27 | // Use pre-defined URI, the only non optional parameter if Apollo should be enabled. 28 | uri, 29 | 30 | // Fetch Credentials: 31 | // - omit: Never send cookies. 32 | // - same-origin: Send user credentials (cookies, basic http auth, etc..) if the URL is on the same origin as the calling script. 33 | // - include: Always send user credentials (cookies, basic http auth, etc..), even for cross-origin calls. 34 | // See also: https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials 35 | credentials: trustNetwork ? "include" : "same-origin" 36 | } 37 | 38 | const httpLink = createHttpLink(finalLinkOptions) 39 | 40 | const middlewareLink = setContext(() => ({ 41 | headers: { 42 | authorization: getAuthHeader ? getAuthHeader() : null 43 | } 44 | })) 45 | 46 | // use with apollo-client 47 | const link = middlewareLink.concat(httpLink) 48 | const cache = new InMemoryCache() 49 | 50 | return new ApolloClient({ 51 | link, 52 | cache, 53 | ...clientOptions 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/views/Counter/Counter.js: -------------------------------------------------------------------------------- 1 | import PropTypes from "prop-types" 2 | import React from "react" 3 | import { connect } from "react-redux" 4 | 5 | import { 6 | decrementCounter, 7 | incrementCounter, 8 | loadCounter 9 | } from "../../modules/Counter" 10 | import Styles from "./Counter.css" 11 | 12 | class Counter extends React.Component { 13 | componentDidMount() { 14 | const { counter, load } = this.props 15 | 16 | // Load only client-side 17 | // Use a fallback e.g. when data not available trigger load 18 | return counter == null ? load() : null 19 | } 20 | 21 | fetchData() { 22 | const { counter, load } = this.props 23 | 24 | // Pre-fetch data on the server 25 | return counter == null ? load() : null 26 | } 27 | 28 | render() { 29 | const { counter, handleDecrement, handleIncrement } = this.props 30 | return ( 31 |
32 |

Counter

33 |

Value: {counter}

34 | 35 | 36 |
37 | ) 38 | } 39 | } 40 | 41 | Counter.propTypes = { 42 | counter: PropTypes.number, 43 | handleIncrement: PropTypes.func, 44 | handleDecrement: PropTypes.func, 45 | load: PropTypes.func 46 | } 47 | 48 | function mapStateToProps(state, ownProps) { 49 | return { 50 | counter: state.counter.value 51 | } 52 | } 53 | 54 | function mapDispatchToProps(dispatch) { 55 | return { 56 | handleIncrement: () => dispatch(incrementCounter()), 57 | handleDecrement: () => dispatch(decrementCounter()), 58 | load: () => dispatch(loadCounter()) 59 | } 60 | } 61 | 62 | export default connect( 63 | mapStateToProps, 64 | mapDispatchToProps 65 | )(Counter) 66 | -------------------------------------------------------------------------------- /packages/edge-webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "edge-webpack", 3 | "version": "0.0.2-alpha.17", 4 | "description": "Centralized Webpack configuration for Edge-based projects.", 5 | "main": "lib/index.cjs.js", 6 | "module": "lib/index.esm.js", 7 | "scripts": { 8 | "test": "jest", 9 | "build": "rimraf lib && preppy", 10 | "prepack": "npm run build" 11 | }, 12 | "files": [ 13 | "lib/" 14 | ], 15 | "author": { 16 | "name": "Sebastian Software", 17 | "email": "s.werner@sebastian-software.de", 18 | "url": "https://www.sebastian-software.de" 19 | }, 20 | "license": "Apache-2.0", 21 | "dependencies": { 22 | "app-manifest-loader": "^2.2.1", 23 | "app-root-dir": "^1.0.2", 24 | "asset-hash": "^2.2.4", 25 | "babel-loader": "^8.0.2", 26 | "cache-loader": "^1.2.2", 27 | "css-loader": "^1.0.0", 28 | "error-overlay-webpack-plugin": "^0.1.5", 29 | "extract-css-chunks-webpack-plugin": "^3.1.1", 30 | "file-loader": "^2.0.0", 31 | "friendly-errors-webpack-plugin": "^1.7.0", 32 | "graphql-tag": "^2.9.2", 33 | "json-loader": "^0.5.7", 34 | "optimize-css-assets-webpack-plugin": "^5.0.1", 35 | "postcss-loader": "^3.0.0", 36 | "source-map-loader": "^0.2.4", 37 | "thread-loader": "^1.2.0", 38 | "uglifyjs-webpack-plugin": "^2.0.1", 39 | "universal-dotenv": "^1.9.1", 40 | "webpack": "^4.19.1", 41 | "webpack-subresource-integrity": "^1.1.0-rc.6", 42 | "yaml-loader": "^0.5.0" 43 | }, 44 | "devDependencies": { 45 | "@babel/core": "^7.1.0", 46 | "babel-core": "^7.0.0-0", 47 | "babel-jest": "^23.6.0", 48 | "babel-preset-edge": "^4.13.1", 49 | "jest": "^23.6.0", 50 | "jest-cli": "^23.6.0", 51 | "jest-preset-edge": "^0.2.1-alpha.18", 52 | "preppy": "^4.4.0", 53 | "rimraf": "^2.6.2" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/edge-express/src/createExpressServer.js: -------------------------------------------------------------------------------- 1 | import express from "express" 2 | 3 | import addSecurityMiddleware from "./addSecurityMiddleware" 4 | import addCoreMiddleware from "./addCoreMiddleware" 5 | import addErrorMiddleware from "./addErrorMiddleware" 6 | import addFallbackHandler from "./addFallbackHandler" 7 | 8 | const defaultLocale = { 9 | default: "en-US", 10 | supported: [ "en-US" ] 11 | } 12 | 13 | const defaultStatic = { 14 | public: "/static/", 15 | path: "build/client" 16 | } 17 | 18 | export default function createExpressServer({ 19 | localeConfig = defaultLocale, 20 | staticConfig = defaultStatic, 21 | afterSecurity = [], 22 | beforeFallback = [] 23 | }) { 24 | // Create our express based server. 25 | const server = express() 26 | 27 | addErrorMiddleware(server) 28 | addSecurityMiddleware(server) 29 | 30 | // Allow for some early additions for middleware 31 | if (afterSecurity.length > 0) { 32 | afterSecurity.forEach((middleware) => { 33 | if (middleware instanceof Array) { 34 | server.use(...middleware) 35 | } else { 36 | server.use(middleware) 37 | } 38 | }) 39 | } 40 | 41 | addCoreMiddleware(server, { locale: localeConfig }) 42 | 43 | // Configure static serving of our webpack bundled client files. 44 | if (staticConfig) { 45 | server.use(staticConfig.public, express.static(staticConfig.path)) 46 | } 47 | 48 | // Allow for some late additions for middleware 49 | if (beforeFallback.length > 0) { 50 | beforeFallback.forEach((middleware) => { 51 | if (middleware instanceof Array) { 52 | server.use(...middleware) 53 | } else { 54 | server.use(middleware) 55 | } 56 | }) 57 | } 58 | 59 | // For all things which did not went well. 60 | addFallbackHandler(server) 61 | 62 | return server 63 | } 64 | -------------------------------------------------------------------------------- /packages/jest-preset-edge/readme.md: -------------------------------------------------------------------------------- 1 | # Jest Preset Edge - Advanced Frontend Focussed Jest Configuration 2 | 3 | ## Features 4 | 5 | - Supports CSS Modules using `identity-obj-proxy`. 6 | - Mocks all asset file requirements for e.g. images, fonts, graphql files, ... 7 | - Includes a mock for the native `fetch()` method. 8 | - Polyfills `requestAnimationFrame()` which is required by React v16. 9 | - Configures Date constructors to return a static data which is very helpful for snapshot testing. 10 | - Integrates a mock for HTML5 canvas so that API calls does not throw inside NodeJS (via JSDOM). 11 | 12 | ## Excludes for Coverage 13 | 14 | - Excludes typical *Edge Platform* based application glue code files e.g. `Application.js`, `State.js` and `Init.js`. 15 | - Excludes *Webpack* and generic bundling/package entry points. 16 | - Excludes *Storybook* stories following the `.story.js` naming convention. 17 | - Excludes *Jest* tests following the `.test.js` naming convention or being placed inside a `__tests__` folder. 18 | 19 | ## Usage 20 | 21 | Install via NPM: 22 | 23 | ``` 24 | npm install --save-dev jest-preset-edge 25 | ``` 26 | 27 | Define the preset inside your own configuration. [Official docs](https://facebook.github.io/jest/docs/en/configuration.html#preset-string): 28 | 29 | ```js 30 | module.exports = { 31 | "preset": "jest-preset-edge" 32 | } 33 | ``` 34 | 35 | This works in a `jest.config.js` or in the `package.json` file under the `jest` key. 36 | 37 | 38 | 39 | ## License 40 | 41 | [Apache License Version 2.0, January 2004](license) 42 | 43 | 44 | ## Copyright 45 | 46 | Logo of Sebastian Software GmbH, Mainz, Germany 47 | 48 | Copyright 2017-2018
[Sebastian Software GmbH](http://www.sebastian-software.de) 49 | -------------------------------------------------------------------------------- /packages/edge-useragent/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "edge-useragent", 3 | "version": "4.0.0-alpha.10", 4 | "description": "High performance user agent string parser for modern environments. Part of the Edge Platform.", 5 | "main": "lib/index.cjs.js", 6 | "module": "lib/index.esm.js", 7 | "keywords": [ 8 | "agent", 9 | "browser", 10 | "browserscope", 11 | "os", 12 | "parse", 13 | "parser", 14 | "ua", 15 | "ua-parse", 16 | "ua-parser", 17 | "user agent", 18 | "user", 19 | "user-agent", 20 | "useragent", 21 | "version" 22 | ], 23 | "engines": { 24 | "node": ">=6.0.0", 25 | "yarn": ">=1.0.0", 26 | "npm": ">=4.0.0" 27 | }, 28 | "files": [ 29 | "bin/", 30 | "docs/", 31 | "lib/" 32 | ], 33 | "author": { 34 | "name": "Sebastian Software", 35 | "email": "s.werner@sebastian-software.de", 36 | "url": "https://www.sebastian-software.de" 37 | }, 38 | "license": "Apache-2.0", 39 | "dependencies": { 40 | "@babel/runtime": "^7.0.0", 41 | "lru-cache": "4.1.3", 42 | "tmp": "0.0.x" 43 | }, 44 | "devDependencies": { 45 | "@babel/node": "^7.0.0", 46 | "@babel/register": "^7.0.0", 47 | "babel-preset-edge": "4.13.1", 48 | "core-js": "^2.5.7", 49 | "cross-env": "^5.2.0", 50 | "eslint": "5.6.0", 51 | "mocha": "5.2.0", 52 | "preppy": "^4.4.0", 53 | "request": "2.88.0", 54 | "rimraf": "*", 55 | "semver": "5.5.x", 56 | "should": "*", 57 | "yamlparser": "0.0.x" 58 | }, 59 | "scripts": { 60 | "lint": "eslint src/", 61 | "test": "cross-env NODE_ENV=test mocha test/*.test.js", 62 | "qa": "cross-env NODE_ENV=test mocha --ui exports test/*.qa.js", 63 | "update": "cross-env NODE_ENV=node babel-node ./bin/update.js && cross-env NODE_ENV=node babel-node ./bin/testfiles.js", 64 | "build": "rimraf lib && preppy", 65 | "prepack": "npm run build" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /packages/edge-core/src/server/renderPage.js: -------------------------------------------------------------------------------- 1 | import serialize from "serialize-javascript" 2 | import Helmet from "react-helmet" 3 | 4 | /** 5 | * Generates a full HTML page containing the render output of the given react element. 6 | * 7 | * @param config {Object} Configuration. 8 | * @param config.state {Object} [{}] The initial state for the redux store which will be used by the 9 | * client to mount the redux store into the desired state. 10 | * @param config.html {string} The rendered HTML content. 11 | * @param config.styles {string} [""] Styles to inject into the page. 12 | * @param config.scripts {string} [""] Scripts to inject into the page. 13 | * @returns The full HTML page in the form of a React element. 14 | */ 15 | export default function renderPage({ html, styles, scripts }) { 16 | if (typeof html !== "string" || html.length === 0) { 17 | throw new Error("[EDGE]: RenderPage: Invalid html string!") 18 | } 19 | 20 | if (typeof styles !== "string" || styles.length === 0) { 21 | throw new Error("[EDGE]: RenderPage: Invalid styles string!") 22 | } 23 | 24 | if (typeof scripts !== "string" || scripts.length === 0) { 25 | throw new Error("[EDGE]: RenderPage: Invalid scripts string!") 26 | } 27 | 28 | const helmet = Helmet.renderStatic() 29 | // const inlineCode = `APP_STATE=${serialize(state, { isJSON: true })};` 30 | // const nonceHtml = edge.nonce ? `nonce="${edge.nonce}"` : "" 31 | 32 | const inlineCode = "" 33 | const nonceHtml = "" 34 | 35 | return ` 36 | 37 | 38 | 39 | ${helmet.title.toString()} 40 | ${helmet.meta.toString()} 41 | ${helmet.link.toString()} 42 | ${styles} 43 | ${helmet.style.toString()} 44 | 45 | 46 |
${html}
47 | 48 | ${scripts} 49 | ${helmet.script.toString()} 50 | 51 | ` 52 | } 53 | -------------------------------------------------------------------------------- /packages/edge-builder/src/commands/build.js: -------------------------------------------------------------------------------- 1 | import webpack from "webpack" 2 | import { remove } from "fs-extra" 3 | import { promisify } from "bluebird" 4 | import formatWebpackMessages from "react-dev-utils/formatWebpackMessages" 5 | import { notify } from "edge-common" 6 | 7 | import builder from "../builder" 8 | 9 | const removePromise = promisify(remove) 10 | 11 | /* eslint-disable no-console, max-params */ 12 | function getWebpackHandler(target, resolve, reject) { 13 | return (fatalError, stats) => { 14 | if (fatalError) { 15 | notify(`Fatal error during compiling ${target}: ${fatalError}`, "error") 16 | return reject() 17 | } 18 | 19 | const rawMessages = stats.toJson({}) 20 | const messages = formatWebpackMessages(rawMessages) 21 | 22 | const isSuccessful = !messages.errors.length && !messages.warnings.length 23 | if (isSuccessful) { 24 | notify(`Compiled ${target} successfully!`, "info") 25 | } 26 | 27 | // If errors exist, only show errors. 28 | if (messages.errors.length) { 29 | notify(`Failed to compile ${target}!`, "error") 30 | console.log(messages.errors.join("\n\n")) 31 | return reject() 32 | } 33 | 34 | return resolve() 35 | } 36 | } 37 | 38 | export function buildClient(config = {}) { 39 | const webpackConfig = builder("client", "production", config) 40 | 41 | return new Promise((resolve, reject) => { 42 | webpack(webpackConfig, getWebpackHandler("client", resolve, reject)) 43 | }) 44 | } 45 | 46 | export function buildServer(config = {}) { 47 | const webpackConfig = builder("server", "production", config) 48 | 49 | return new Promise((resolve, reject) => { 50 | webpack(webpackConfig, getWebpackHandler("server", resolve, reject)) 51 | }) 52 | } 53 | 54 | export function cleanServer(config = {}) { 55 | return removePromise(config.output.server) 56 | } 57 | 58 | export function cleanClient(config = {}) { 59 | return removePromise(config.output.client) 60 | } 61 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/__snapshots__/fixes.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Autoprefixer 1`] = ` 4 | ":-webkit-full-screen a{ 5 | display:flex 6 | } 7 | 8 | :-moz-full-screen a{ 9 | display:flex 10 | } 11 | 12 | :-ms-fullscreen a{ 13 | display:-ms-flexbox; 14 | display:flex 15 | } 16 | 17 | :fullscreen a{ 18 | display:-ms-flexbox; 19 | display:flex 20 | }" 21 | `; 22 | 23 | exports[`Flexbox Fix 1`] = ` 24 | ".elem{ 25 | -ms-flex:1 1; 26 | flex:1 1 27 | }" 28 | `; 29 | 30 | exports[`Font Variant 1`] = ` 31 | "h2{ 32 | -webkit-font-feature-settings:\\"c2sc\\"; 33 | font-feature-settings:\\"c2sc\\"; 34 | font-variant-caps:small-caps 35 | } 36 | 37 | table{ 38 | -webkit-font-feature-settings:\\"lnum\\"; 39 | font-feature-settings:\\"lnum\\"; 40 | font-variant-numeric:lining-nums 41 | }" 42 | `; 43 | 44 | exports[`Gradient Fix 1`] = ` 45 | ".elem{ 46 | background-image:linear-gradient(green,rgba(0,128,0,0)) 47 | }" 48 | `; 49 | 50 | exports[`Input Style Fixes 1`] = ` 51 | "input[type=range]::-webkit-slider-runnable-track{ 52 | -webkit-appearance:none; 53 | height:3px 54 | } 55 | 56 | input[type=range]::-moz-range-track{ 57 | -moz-appearance:none; 58 | height:3px 59 | } 60 | 61 | input[type=range]::-ms-track{ 62 | height:3px 63 | } 64 | 65 | input[type=range]{ 66 | -webkit-appearance:none 67 | } 68 | 69 | input[type=range]::-webkit-slider-thumb{ 70 | -webkit-appearance:none; 71 | width:16px; 72 | height:8px 73 | } 74 | 75 | input[type=range]::-moz-range-thumb{ 76 | -moz-appearance:none; 77 | width:16px; 78 | height:8px 79 | } 80 | 81 | input[type=range]::-ms-thumb{ 82 | width:16px; 83 | height:8px 84 | } 85 | 86 | input[type=range]::-moz-focus-outer{ 87 | border:0 88 | }" 89 | `; 90 | 91 | exports[`Pseudoelements 1`] = ` 92 | "a:before{ 93 | color:#ff4136 94 | }" 95 | `; 96 | 97 | exports[`Selector matches() 1`] = ` 98 | "p.special,p:first-child{ 99 | color:#ff4136 100 | }" 101 | `; 102 | -------------------------------------------------------------------------------- /packages/jest-preset-edge/setup.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-commonjs, filenames/match-regex */ 2 | /* global jest */ 3 | 4 | // Make sure that environment variables are available 5 | require("universal-dotenv") 6 | 7 | // Mocking Canvas APIs in a very lean way. We don't really need some 8 | // Cairo based rendering at all in most test scenarios. 9 | require("jest-canvas-mock") 10 | 11 | // Mocking all fetch() calls. Should never depend on any actual network during test. 12 | global.fetch = require("jest-fetch-mock") 13 | 14 | // Rewrite console.log/debug to a mock. This shouldn't be required to run in tests 15 | // and makes test runner output much more calm and focused. We keep warn/error and 16 | // other not so often used methods intact. 17 | console.clear = jest.fn() 18 | console.log = jest.fn() 19 | console.debug = jest.fn() 20 | 21 | // Mock Date.now() so that all values are static 22 | require("jest-mock-now")(new Date("2018-05-17T11:25:51.054Z")) 23 | 24 | // Polyfill for RequestAnimationFrame which is required for React v16 25 | require("raf/polyfill") 26 | 27 | // Making sure that global.URL is supported by loading the Polyfill. 28 | // This is required for some libraries like the MapBox GL API. 29 | if (global.URL == null) { 30 | require("url-polyfill") 31 | } 32 | 33 | // Polyfill for not yet implemented parts of the URL API in NodeJS. 34 | // See also: https://github.com/nodejs/node/issues/16167 35 | if (URL.createObjectURL == null) { 36 | URL.createObjectURL = jest.fn() 37 | URL.revokeObjectURL = jest.fn() 38 | } 39 | 40 | // Mocking for Element Resize Detector which is used for responsive components 41 | // e.g. via React Sizeme. We have to pass the factory as otherwise only the 42 | // root function is mocked which is not enough. 43 | try { 44 | jest.mock("element-resize-detector", () => { 45 | return () => ({ 46 | listenTo: jest.fn(), 47 | removeListener: jest.fn(), 48 | removeAllListeners: jest.fn(), 49 | uninstall: jest.fn() 50 | }) 51 | }) 52 | } catch(error) {} 53 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "edge-boilerplate", 3 | "description": "Edge Boilerplate is a template for modern web applications. Part of the Edge Platform.", 4 | "version": "1.0.0-alpha.34", 5 | "main": "server/index.js", 6 | "author": { 7 | "name": "Sebastian Software", 8 | "email": "s.werner@sebastian-software.de", 9 | "url": "https://www.sebastian-software.de" 10 | }, 11 | "bin": { 12 | "server": "bin/server" 13 | }, 14 | "files": [ 15 | "docs/" 16 | ], 17 | "scripts": { 18 | "dev": "node -r esm src/dev.js", 19 | "build": "prepublish && edge build", 20 | "start": "node bin/server", 21 | "clean": "rimraf bin && rimraf build && rimraf docs", 22 | "test": "jest", 23 | "docs": "npm run styleguide:build && npm run storybook:build", 24 | "storybook": "start-storybook --port 1449 --config-dir ../../node_modules/edge-storybook/lib", 25 | "storybook:build": "rimraf docs/storybook && build-storybook --output-dir docs/storybook --config-dir ../../node_modules/edge-storybook/lib" 26 | }, 27 | "keywords": [ 28 | "front-end", 29 | "boilerplate", 30 | "webpack", 31 | "react", 32 | "ssr", 33 | "universal", 34 | "graphql", 35 | "apollo" 36 | ], 37 | "license": "Apache-2.0", 38 | "dependencies": { 39 | "edge-common": "^1.0.0-alpha.10", 40 | "edge-express": "^1.0.0-alpha.10", 41 | "http-status-codes": "^1.3.0", 42 | "universal-dotenv": "^1.9.1", 43 | "yn": "^2.0" 44 | }, 45 | "devDependencies": { 46 | "@reach/router": "^1.1.1", 47 | "@storybook/react": "^4.0.0-alpha.3", 48 | "cookiesjs": "^2.0.0", 49 | "edge-builder": "^1.0.0-alpha.12", 50 | "edge-core": "^1.0.0-alpha.13", 51 | "edge-postcss": "^1.0.0-alpha.14", 52 | "edge-storybook": "^1.0.0-alpha.32", 53 | "edge-style": "^1.0.0-alpha.14", 54 | "eslint": "^5.6.0", 55 | "esm": "^3.0.84", 56 | "flow": "^0.2.3", 57 | "jest": "^23.6.0", 58 | "jest-preset-edge": "^0.2.1-alpha.18", 59 | "prepublish": "2.2.0", 60 | "react": "^16.5.2", 61 | "react-dom": "^16.5.2" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/views/Localization/Localization.js: -------------------------------------------------------------------------------- 1 | import cookies from "cookiesjs" 2 | import Helmet from "react-helmet" 3 | import PropTypes from "prop-types" 4 | import React from "react" 5 | import { connect } from "react-redux" 6 | import { 7 | FormattedDate, 8 | FormattedNumber, 9 | FormattedRelative, 10 | FormattedTime 11 | } from "react-intl" 12 | import { getLocale } from "edge-core" 13 | 14 | import Styles from "./Localization.css" 15 | 16 | const time = new Date() 17 | const timeDiff = 100000 18 | 19 | function switchLocale(locale) { 20 | cookies({ locale }) 21 | window.location.reload() 22 | } 23 | 24 | function Localization({ locale }) { 25 | return ( 26 |
27 | 28 |

Locale: {locale}

29 |
    30 |
  • 31 | FormattedNumber: 32 |
  • 33 |
  • 34 | FormattedNumber: 35 |
  • 36 |
  • 37 | FormattedDate: 38 |
  • 39 |
  • 40 | FormattedTime: 41 |
  • 42 |
  • 43 | FormattedRelative: 44 |
  • 45 |
46 |

Select Locale:

47 | 64 |
65 | ) 66 | } 67 | 68 | Localization.propTypes = { 69 | locale: PropTypes.string 70 | } 71 | 72 | function mapStateToProps(state, ownProps) { 73 | return { 74 | locale: getLocale(state) 75 | } 76 | } 77 | 78 | function mapDispatchToProps(dispatch) { 79 | return {} 80 | } 81 | 82 | export default connect( 83 | mapStateToProps, 84 | mapDispatchToProps 85 | )(Localization) 86 | -------------------------------------------------------------------------------- /packages/edge-express/readme.md: -------------------------------------------------------------------------------- 1 | # Edge Express
[![Sponsored by][sponsor-img]][sponsor] [![Version][npm-version-img]][npm] [![Downloads][npm-downloads-img]][npm] [![Build Status Unix][travis-img]][travis] [![Build Status Windows][appveyor-img]][appveyor] [![Dependencies][deps-img]][deps] 2 | 3 | [sponsor-img]: https://img.shields.io/badge/Sponsored%20by-Sebastian%20Software-692446.svg 4 | [sponsor]: https://www.sebastian-software.de 5 | [deps]: https://david-dm.org/sebastian-software/edge-express 6 | [deps-img]: https://david-dm.org/sebastian-software/edge-express.svg 7 | [npm]: https://www.npmjs.com/package/edge-express 8 | [npm-downloads-img]: https://img.shields.io/npm/dm/edge-express.svg 9 | [npm-version-img]: https://img.shields.io/npm/v/edge-express.svg 10 | [travis-img]: https://img.shields.io/travis/sebastian-software/edge-express/master.svg?branch=master&label=unix%20build 11 | [appveyor-img]: https://img.shields.io/appveyor/ci/swernerx/edge-express/master.svg?label=windows%20build 12 | [travis]: https://travis-ci.org/sebastian-software/edge-express 13 | [appveyor]: https://ci.appveyor.com/project/swernerx/edge-express/branch/master 14 | 15 | Edge Express is a centralized Express-based HTTP server with sophisticated built-in security. Part of the Edge Platform. 16 | 17 | > The Edge Platform helps you focus on business logic rather than dealing with massive tooling, common patterns, complex configurations. 18 | 19 | ## Features 20 | 21 | - Express Security with Helmet and HPP. 22 | - Improved Express Error Handling. 23 | - Custom "setup" and "dynamic" middleware as needed. 24 | - Support for Body-Parser, JSON, Cookies and Static serving out-of-the-box. 25 | - Bundled for Node6 + Node8 (Different Library Outputs). 26 | 27 | 28 | ## Public API 29 | 30 | - `addCoreMiddleware()` 31 | - `addErrorMiddleware()` 32 | - `addFallbackHandler()` 33 | - `addSecurityMiddleware()` 34 | - `createExpressServer()` 35 | 36 | 37 | 38 | ## License 39 | 40 | [Apache License Version 2.0, January 2004](license) 41 | 42 | 43 | ## Copyright 44 | 45 | Logo of Sebastian Software GmbH, Mainz, Germany 46 | 47 | Copyright 2017-2018
[Sebastian Software GmbH](http://www.sebastian-software.de) 48 | -------------------------------------------------------------------------------- /packages/edge-webpack/src/index.js: -------------------------------------------------------------------------------- 1 | import { resolve } from "path" 2 | 3 | // Basic Configuration Adapter 4 | import { IS_DEVELOPMENT, BUILD_TARGET } from "./config" 5 | 6 | // Individual Feature Modules 7 | import EnvironmentModule from "./modules/Environment" 8 | import LocalesModule from "./modules/Locales" 9 | import ExperienceModule from "./modules/Experience" 10 | import OptimizationModule from "./modules/Optimization" 11 | import RulesModule from "./modules/Rules" 12 | import StaticModule from "./modules/Static" 13 | 14 | import { Hasher } from "asset-hash" 15 | 16 | const node = BUILD_TARGET === "client" ? { 17 | fs: "empty", 18 | __filename: "mock", 19 | __dirname: "mock" 20 | } : null 21 | 22 | // For usage in otherwise pre-defined Webpack environment like Storybook 23 | export const core = (options = {}) => ({ 24 | module: { 25 | rules: RulesModule.rules 26 | }, 27 | 28 | node, 29 | 30 | stats: ExperienceModule.stats, 31 | serve: ExperienceModule.serve, 32 | devServer: ExperienceModule.devServer, 33 | 34 | plugins: [ 35 | ...EnvironmentModule.plugins, 36 | ...LocalesModule.plugins, 37 | ...RulesModule.plugins 38 | ].filter(Boolean) 39 | }) 40 | 41 | export const full = (options = {}) => ({ 42 | name: EnvironmentModule.name, 43 | mode: EnvironmentModule.mode, 44 | entry: { 45 | main: [ resolve(options.root || process.env.APP_ROOT, `src/${BUILD_TARGET}/index.js`) ] 46 | }, 47 | 48 | node, 49 | 50 | output: { 51 | path: resolve(options.root || process.env.APP_ROOT, "dist"), 52 | filename: IS_DEVELOPMENT ? "index.js" : "index.[hash].js", 53 | chunkFilename: IS_DEVELOPMENT ? 54 | "chunk-[name].[chunkhash].js" : 55 | "chunk-[name].[chunkhash].js", 56 | crossOriginLoading: "anonymous", 57 | hashFunction: Hasher, 58 | hashDigest: "base52" 59 | }, 60 | 61 | module: { 62 | rules: RulesModule.rules 63 | }, 64 | 65 | stats: ExperienceModule.stats, 66 | serve: ExperienceModule.serve, 67 | devServer: ExperienceModule.devServer, 68 | 69 | optimization: OptimizationModule.optimization, 70 | 71 | plugins: [ 72 | ...EnvironmentModule.plugins, 73 | ...LocalesModule.plugins, 74 | ...ExperienceModule.plugins, 75 | ...RulesModule.plugins, 76 | ...StaticModule.plugins 77 | ].filter(Boolean) 78 | }) 79 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/components/htmlhead/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/modules/Counter.js: -------------------------------------------------------------------------------- 1 | export const SET_COUNTER = "counter/SET" 2 | export const INCREMENT_COUNTER = "counter/INCREMENT" 3 | export const DECREMENT_COUNTER = "counter/DECREMENT" 4 | 5 | /** 6 | * Selector for accessing the counter value from inside the global state. 7 | * 8 | * @param {state} state Global Redux state. 9 | */ 10 | export function getCounter(state) { 11 | return state.counter.value 12 | } 13 | 14 | /** 15 | * Action creator for setting the counter value. 16 | * 17 | * @param {number} value New value to set for the counter. 18 | */ 19 | export function setCounter(value) { 20 | return { type: SET_COUNTER, value } 21 | } 22 | 23 | /** 24 | * Action creator for incrementing the counter value. 25 | */ 26 | export function incrementCounter() { 27 | return { type: INCREMENT_COUNTER } 28 | } 29 | 30 | /** 31 | * Action creator for decrementing the counter value. 32 | */ 33 | export function decrementCounter() { 34 | return { type: DECREMENT_COUNTER } 35 | } 36 | 37 | /** 38 | * This somewhat tries to emulate a asyncronous backend request. 39 | */ 40 | function mockServerDelay() { 41 | /* eslint-disable no-console */ 42 | console.log("Loading counter...") 43 | return new Promise((resolve, reject) => { 44 | setTimeout(() => { 45 | let value = Math.round(Math.random() * 100) 46 | console.log("Received counter:", value) 47 | resolve(value) 48 | }, 100) 49 | }) 50 | } 51 | 52 | /** 53 | * Async data loading using redux-thunk. 54 | */ 55 | export function loadCounter() { 56 | return (dispatch) => 57 | mockServerDelay().then((value) => dispatch(setCounter(value))) 58 | } 59 | 60 | const initialState = { value: null } 61 | 62 | /** 63 | * Reducer for all counter relevant action types. 64 | * 65 | * @param previousState Previous state object of this reducer. 66 | * @param {string} action Action to process. 67 | */ 68 | export function counterReducer(previousState = initialState, action) { 69 | switch (action.type) { 70 | case SET_COUNTER: 71 | return { ...previousState, value: action.value } 72 | 73 | case INCREMENT_COUNTER: 74 | return { ...previousState, value: previousState.value + 1 } 75 | 76 | case DECREMENT_COUNTER: 77 | return { ...previousState, value: previousState.value - 1 } 78 | 79 | default: 80 | return previousState 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /packages/edge-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "edge-core", 3 | "version": "1.0.0-alpha.13", 4 | "description": "Core JavaScript Infrastructure for Edge. Part of the Edge Platform.", 5 | "main": "lib/node.es5.cjs.js", 6 | "module": "lib/node.es5.esm.js", 7 | "browser": "lib/browser.es5.esm.js", 8 | "main:es2015": "lib/node.es2015.cjs.js", 9 | "module:es2015": "lib/node.es2015.esm.js", 10 | "browser:es2015": "lib/browser.es2015.esm.js", 11 | "scripts": { 12 | "size": "npm run build", 13 | "test": "jest", 14 | "build": "rimraf lib && prepublish", 15 | "prepack": "npm run build" 16 | }, 17 | "engines": { 18 | "node": ">=6.0.0", 19 | "yarn": ">=0.21.3", 20 | "npm": ">=4.0.0" 21 | }, 22 | "files": [ 23 | "bin/", 24 | "docs/", 25 | "lib/" 26 | ], 27 | "keywords": [ 28 | "react", 29 | "apollo", 30 | "graphql", 31 | "intl", 32 | "localization", 33 | "framework" 34 | ], 35 | "author": { 36 | "name": "Sebastian Software", 37 | "email": "s.werner@sebastian-software.de", 38 | "url": "https://www.sebastian-software.de" 39 | }, 40 | "license": "Apache-2.0", 41 | "dependencies": { 42 | "apollo-cache-inmemory": "^1.3.0-beta.6", 43 | "apollo-client": "^2.4.2", 44 | "apollo-link-context": "^1.0.9", 45 | "apollo-link-http": "^1.5.5", 46 | "bluebird": "^3.5.2", 47 | "caniuse-lite": "^1.0.30000885", 48 | "chalk": "^2.4.1", 49 | "edge-useragent": "^4.0.0-alpha.10", 50 | "graphql": "^14.0.2", 51 | "identity-obj-proxy": "^3.0.0", 52 | "intl-locales-supported": "^1.0.0", 53 | "lean-intl": "^4.0.2", 54 | "node-fetch": "^2.2.0", 55 | "prop-types": "^15.6.2", 56 | "query-string": "^6.1.0", 57 | "raf": "^3.4.0", 58 | "react": "^16.5.2", 59 | "react-apollo": "^2.1.11", 60 | "react-dom": "^16.5.2", 61 | "react-helmet": "^5.2.0", 62 | "react-intl": "^2.6.0", 63 | "react-redux": "^5.0.7", 64 | "react-test-renderer": "^16.5.2", 65 | "react-tree-walker": "^4.3.0", 66 | "redux": "^4.0.0", 67 | "redux-immutable-state-invariant": "^2.1.0", 68 | "redux-logger": "^3.0.6", 69 | "redux-thunk": "^2.3.0", 70 | "serialize-javascript": "^1.5.0", 71 | "source-map-support": "^0.5.9", 72 | "unfetch": "^4.0.1", 73 | "webpack-flush-chunks": "^2.0.1" 74 | }, 75 | "devDependencies": { 76 | "jest": "23.6.0", 77 | "jest-fetch-mock": "^1.6.6", 78 | "prepublish": "2.2.0" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /packages/edge-builder/readme.md: -------------------------------------------------------------------------------- 1 | # Edge Builder
[![Sponsored by][sponsor-img]][sponsor] [![Version][npm-version-img]][npm] [![Downloads][npm-downloads-img]][npm] [![Build Status Unix][travis-img]][travis] [![Build Status Windows][appveyor-img]][appveyor] [![Dependencies][deps-img]][deps] 2 | 3 | [sponsor-img]: https://img.shields.io/badge/Sponsored%20by-Sebastian%20Software-692446.svg 4 | [sponsor]: https://www.sebastian-software.de 5 | [deps]: https://david-dm.org/sebastian-software/edge-builder 6 | [deps-img]: https://david-dm.org/sebastian-software/edge-builder.svg 7 | [npm]: https://www.npmjs.com/package/edge-builder 8 | [npm-downloads-img]: https://img.shields.io/npm/dm/edge-builder.svg 9 | [npm-version-img]: https://img.shields.io/npm/v/edge-builder.svg 10 | [travis-img]: https://img.shields.io/travis/sebastian-software/edge-builder/master.svg?branch=master&label=unix%20build 11 | [appveyor-img]: https://img.shields.io/appveyor/ci/swernerx/edge-builder/master.svg?label=windows%20build 12 | [travis]: https://travis-ci.org/sebastian-software/edge-builder 13 | [appveyor]: https://ci.appveyor.com/project/swernerx/edge-builder/branch/master 14 | 15 | Edge Builder is a build tool for modern web applications. Part of the Edge Platform. 16 | 17 | > The Edge Platform helps you focus on business logic rather than dealing with massive tooling, common patterns, complex configurations. 18 | 19 | ## Features 20 | 21 | - Semi-Automatic Code-Splitting for both CSS and JS. 22 | - Hot Loading for Client and Server using Webpack Multi Compiler Architecture. 23 | - PostCSS powered CSS pipeline with Sass-inspired features. 24 | - CSS Modules for Component Style Isolation. 25 | - Build Caching using Webpacks Cache-Loader. 26 | - Efficient Long-Term-Caching using Hashed File Names. 27 | 28 | 29 | ## Technology 30 | 31 | - [webpack-flush-chunks](https://github.com/faceyspacey/webpack-flush-chunks) 32 | - [react-universal-component](https://github.com/faceyspacey/react-universal-component) 33 | - [extract-css-chunks-webpack-plugin](https://github.com/faceyspacey/extract-css-chunks-webpack-plugin) 34 | 35 | 36 | 37 | ## License 38 | 39 | [Apache License Version 2.0, January 2004](license) 40 | 41 | 42 | ## Copyright 43 | 44 | Logo of Sebastian Software GmbH, Mainz, Germany 45 | 46 | Copyright 2017-2018
[Sebastian Software GmbH](http://www.sebastian-software.de) 47 | -------------------------------------------------------------------------------- /packages/edge-style/src/OpenType.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable selector-max-type, declaration-colon-newline-after, max-line-length */ 2 | html, 3 | body, 4 | table { 5 | font-feature-settings: "kern" 1, "liga" 1, "calt" 1, "pnum" 1, "tnum" 0, "onum" 1, "lnum" 0, "dlig" 0; /* 2. */ 6 | } 7 | 8 | /** 9 | * Turn on discretionary ligatures for larger headings 10 | */ 11 | 12 | h1, 13 | h2, 14 | h3 { 15 | font-feature-settings: "kern" 1, "liga" 1, "calt" 1, "pnum" 1, "tnum" 0, "onum" 1, "lnum" 0, "dlig" 1; 16 | } 17 | 18 | /** 19 | * 1. Change all letters to uppercase 20 | * 2. Turn on small caps for upper and lowercase letters 21 | */ 22 | 23 | abbr { 24 | text-transform: uppercase; /* 1 */ 25 | font-feature-settings: "kern" 1, "liga" 1, "calt" 1, "pnum" 1, "tnum" 0, "onum" 1, "lnum" 0, "smcp" 1, "c2sc" 1; /* 2 */ 26 | } 27 | 28 | time { 29 | font-feature-settings: "kern" 1, "liga" 1, "calt" 1, "pnum" 1, "tnum" 0, "onum" 1, "lnum" 0; 30 | } 31 | 32 | /* 33 | * Turn off kerning and ligatures, 34 | * Turn on lining, tabular numerals, slashed zero 35 | */ 36 | 37 | pre, 38 | kbd, 39 | samp, 40 | code { 41 | font-feature-settings: "kern" 0, "liga" 0, "calt" 1, "dlig" 0, "pnum" 0, "tnum" 1, "onum" 0, "lnum" 1, "zero" 1; 42 | } 43 | 44 | /** 45 | * Turn on proper supercript numerals 46 | */ 47 | 48 | sup { 49 | font-feature-settings: "kern" 1, "liga" 1, "calt" 1, "pnum" 1, "tnum" 0, "onum" 1, "lnum" 0, "dlig" 0, "sups" 1; 50 | } 51 | 52 | /** 53 | * Turn on proper subscript numerals 54 | */ 55 | 56 | sub { 57 | font-feature-settings: "kern" 1, "liga" 1, "calt" 1, "pnum" 1, "tnum" 0, "onum" 1, "lnum" 0, "dlig" 0, "subs" 1; 58 | } 59 | 60 | /** 61 | * Turns on lining, proportional numerals without clarified zeroes 62 | * TODO: Additional input modes https://developer.mozilla.org/en/docs/Web/HTML/Element/Input#attr-inputmode 63 | */ 64 | 65 | input[type="color"], 66 | input[type="date"], 67 | input[type="datetime"], 68 | input[type="datetime-local"], 69 | input[type="number"], 70 | input[type="range"], 71 | input[type="tel"], 72 | input[type="week"] { 73 | font-feature-settings: "kern" 0, "liga" 1, "calt" 1, "pnum" 1, "tnum" 0, "onum" 0, "lnum" 1, "zero" 0; 74 | } 75 | 76 | /* Tables 77 | ========================================================================== */ 78 | 79 | /** 80 | * Turns on tabular, lining numerals and slashed zero 81 | */ 82 | 83 | tbody, 84 | caption { 85 | font-feature-settings: "kern" 1, "liga" 1, "calt" 1, "pnum" 0, "tnum" 1, "onum" 0, "lnum" 1, "zero" 1; 86 | } 87 | -------------------------------------------------------------------------------- /packages/edge-style/src/Sanitize.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable selector-max-universal, selector-max-type */ 2 | 3 | /** 4 | * Add text decoration inheritance in all browsers. 5 | * Add vertical alignment inheritence in all browsers. 6 | */ 7 | 8 | ::before, 9 | ::after { 10 | text-decoration: inherit; 11 | vertical-align: inherit; 12 | } 13 | 14 | /** 15 | * Set `background-repeat: no-repeat` to all elements and pseudo elements. 16 | */ 17 | 18 | *, 19 | ::before, 20 | ::after { 21 | background-repeat: no-repeat; 22 | } 23 | 24 | /** 25 | * Remove the list style on navigation lists in all browsers. 26 | */ 27 | 28 | nav ol, 29 | nav ul { 30 | list-style: none; 31 | } 32 | 33 | /** 34 | * Change the alignment on media elements in all browers. 35 | */ 36 | 37 | audio, 38 | canvas, 39 | iframe, 40 | img, 41 | svg, 42 | video { 43 | vertical-align: middle; 44 | } 45 | 46 | /** 47 | * Change the fill color to match the text color in all browsers. 48 | */ 49 | 50 | svg { 51 | fill: currentColor; 52 | } 53 | 54 | /** 55 | * Inherit color and font styling in all browsers. 56 | */ 57 | 58 | button, 59 | input, 60 | optgroup, 61 | select, 62 | textarea { 63 | color: inherit; 64 | font: inherit; 65 | line-height: inherit; 66 | } 67 | 68 | /** 69 | * Restore the font weight unset by the previous rule. 70 | */ 71 | 72 | optgroup { 73 | /* bold == 700 */ 74 | font-weight: 700; 75 | } 76 | 77 | /** 78 | * Change the resize direction on textareas in all browsers. 79 | */ 80 | 81 | textarea { 82 | resize: vertical; 83 | } 84 | 85 | /* 86 | * Remove the tapping delay on clickable elements. 87 | */ 88 | 89 | a, 90 | area, 91 | button, 92 | input, 93 | label, 94 | select, 95 | summary, 96 | textarea, 97 | [tabindex] { 98 | touch-action: manipulation; 99 | } 100 | 101 | /** 102 | * 1. Inherit style issues with custom selections, per 103 | * robsterlini.co.uk/journal/opentype-and-selection-dont-mix 104 | * 2. Turn on kerning, standard ligatures, and proportional, oldstyle numerals 105 | * Turn off all other ligatures, tabular, lining numerals, and alternates 106 | * Uses same settings for tables 107 | * 3. Hard-codes fallback text selection for issue #18, color is Chrome’s 108 | * per via http://stackoverflow.com/a/16094931/864799 109 | */ 110 | 111 | ::selection { 112 | background-color: #accef7; /* 3. */ 113 | color: inherit; /* 1. */ 114 | text-shadow: inherit; /* 2. */ 115 | } 116 | -------------------------------------------------------------------------------- /packages/edge-builder/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "edge-builder", 3 | "description": "Edge Builder is a build tool for modern web applications. Part of the Edge Platform.", 4 | "version": "1.0.0-alpha.12", 5 | "author": { 6 | "name": "Sebastian Software", 7 | "email": "s.werner@sebastian-software.de", 8 | "url": "https://www.sebastian-software.de" 9 | }, 10 | "bin": { 11 | "edge": "bin/edge" 12 | }, 13 | "main": "lib/index.cjs.js", 14 | "module": "lib/index.esm.js", 15 | "license": "Apache-2.0", 16 | "scripts": { 17 | "prepack": "npm run build", 18 | "build": "rimraf bin && rimraf lib && prepublish", 19 | "test": "echo" 20 | }, 21 | "engines": { 22 | "node": ">=6.0.0", 23 | "yarn": ">=1.0.0", 24 | "npm": ">=4.0.0" 25 | }, 26 | "dependencies": { 27 | "app-manifest-loader": "^2.2.1", 28 | "app-root-dir": "^1.0.2", 29 | "babel-loader": "^8.0.2", 30 | "babel-minify-webpack-plugin": "^0.3.1", 31 | "babel-preset-edge": "^4.13.1", 32 | "bluebird": "^3.5.2", 33 | "builtin-modules": "^3.0.0", 34 | "cache-loader": "^1.2.2", 35 | "case-sensitive-paths-webpack-plugin": "^2.1.2", 36 | "chalk": "^2.4.1", 37 | "clipboardy": "^1.2.3", 38 | "css-loader": "^1.0.0", 39 | "edge-common": "^1.0.0-alpha.10", 40 | "extract-css-chunks-webpack-plugin": "^3.1.1", 41 | "file-loader": "^2.0.0", 42 | "fs-extra": "^7.0.0", 43 | "graphql": "^14.0.2", 44 | "graphql-tag": "^2.9.2", 45 | "html-webpack-plugin": "^3.2.0", 46 | "jsome": "^2.5.0", 47 | "json-loader": "^0.5.7", 48 | "lean-nodent-runtime": "^1.0.2", 49 | "loader-utils": "^1.1.0", 50 | "lodash": "^4.17.11", 51 | "meow": "^5.0.0", 52 | "ora": "^3.0.0", 53 | "postcss-loader": "^3.0.0", 54 | "react-dev-utils": "^5.0.2", 55 | "resolve-pkg": "^1.0.0", 56 | "source-map-loader": "^0.2.4", 57 | "stats-webpack-plugin": "^0.7.0", 58 | "uglify-es": "^3.3.10", 59 | "uglifyjs-webpack-plugin": "^2.0.1", 60 | "update-notifier": "^2.5.0", 61 | "webpack": "^4.19.1", 62 | "webpack-bundle-analyzer": "^3.0.2", 63 | "webpack-dev-middleware": "^3.3.0", 64 | "webpack-flush-chunks": "^2.0.1", 65 | "webpack-hot-middleware": "^2.24.0", 66 | "webpack-hot-server-middleware": "^0.5.0", 67 | "webpack-subresource-integrity": "^1.1.0-rc.6", 68 | "worker-loader": "^2.0.0", 69 | "yaml-loader": "^0.5.0", 70 | "yn": "^2.0.0" 71 | }, 72 | "devDependencies": { 73 | "jest": "23.6.0", 74 | "prepublish": "2.2.0", 75 | "rimraf": "*" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/__snapshots__/layout.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Flow Root 1`] = ` 4 | ".selector{ 5 | display:flow-root; 6 | -webkit-column-count:1; 7 | -moz-column-count:1; 8 | column-count:1 9 | }" 10 | `; 11 | 12 | exports[`Grid KISS 1`] = ` 13 | ".gridTest{ 14 | display:grid; 15 | -ms-flex-line-pack:justify; 16 | align-content:space-between; 17 | grid-template:\\"a a\\" 120px \\"b c \\" 1fr \\"e e\\" 60px/30% 1fr 18 | } 19 | 20 | .gridTest>header{ 21 | grid-area:a; 22 | -ms-flex-item-align:start; 23 | align-self:start 24 | } 25 | 26 | .gridTest>.sidebar{ 27 | grid-area:b 28 | } 29 | 30 | .gridTest>main{ 31 | grid-area:c 32 | } 33 | 34 | .gridTest>footer{ 35 | grid-area:e; 36 | justify-self:center; 37 | -ms-flex-item-align:end; 38 | align-self:end 39 | } 40 | 41 | @supports not (grid-template-areas:\\"test\\"){ 42 | .gridTest{ 43 | position:relative; 44 | display:block; 45 | width:100%; 46 | height:100% 47 | } 48 | 49 | .gridTest>*{ 50 | position:absolute; 51 | box-sizing:border-box 52 | } 53 | 54 | .gridTest>header{ 55 | max-height:120px; 56 | width:100%; 57 | top:0; 58 | left:0 59 | } 60 | 61 | .gridTest>.sidebar,.gridTest>main{ 62 | height:calc(100% - 180px); 63 | width:30%; 64 | top:120px; 65 | left:0 66 | } 67 | 68 | .gridTest>main{ 69 | width:70%; 70 | left:30% 71 | } 72 | 73 | .gridTest>footer{ 74 | transform:translateX(-50%); 75 | max-height:60px; 76 | max-width:100%; 77 | bottom:0; 78 | left:50% 79 | } 80 | } 81 | 82 | @media screen and (min-width:0\\\\0){ 83 | .gridTest{ 84 | position:relative; 85 | display:block; 86 | width:100%; 87 | height:100% 88 | } 89 | 90 | .gridTest>*{ 91 | position:absolute; 92 | box-sizing:border-box 93 | } 94 | 95 | .gridTest>header{ 96 | max-height:120px; 97 | width:100%; 98 | top:0; 99 | left:0 100 | } 101 | 102 | .gridTest>.sidebar,.gridTest>main{ 103 | height:calc(100% - 180px); 104 | width:30%; 105 | top:120px; 106 | left:0 107 | } 108 | 109 | .gridTest>main{ 110 | width:70%; 111 | left:30% 112 | } 113 | 114 | .gridTest>footer{ 115 | transform:translateX(-50%); 116 | max-height:60px; 117 | max-width:100%; 118 | bottom:0; 119 | left:50% 120 | } 121 | }" 122 | `; 123 | 124 | exports[`Lost Grid 1`] = ` 125 | ".grid{ 126 | width:calc(24.975% - 22.5px) 127 | } 128 | 129 | .grid:nth-child(n){ 130 | float:left; 131 | margin-right:30px; 132 | clear:none 133 | } 134 | 135 | .grid:last-child{ 136 | margin-right:0 137 | } 138 | 139 | .grid:nth-child(12n){ 140 | margin-right:0; 141 | float:right 142 | } 143 | 144 | .grid:nth-child(12n+1){ 145 | clear:both 146 | }" 147 | `; 148 | -------------------------------------------------------------------------------- /packages/edge-builder/src/plugins/ChunkNames.js: -------------------------------------------------------------------------------- 1 | import path from "path" 2 | import { getHashDigest } from "loader-utils" 3 | 4 | const WORKING_DIR = process.cwd() 5 | const HASH_TYPE = "sha256" 6 | const DIGEST_TYPE = "base62" 7 | const DIGEST_LENGTH = 4 8 | const SCRIPT_EXTENSIONS = new Set([ ".mjs", ".js", ".jsx", ".ts", ".tsx" ]) 9 | const SKIP_FOLDERS = [ "lib", "dist", "src", "build" ] 10 | 11 | function generateChunkName(request, rawRequest) { 12 | // Strip prefixed loader syntax from Webpack 13 | const splittedRequest = request.split("!") 14 | const cleanRequest = splittedRequest[splittedRequest.length - 1] 15 | 16 | // Getting relative path inside working directory 17 | let relative = path.relative(WORKING_DIR, cleanRequest) 18 | 19 | const isExternal = relative.startsWith(`node_modules${ path.sep}`) 20 | if (isExternal) { 21 | // if the module is an DelegatedModule, the rawRequest will be undefined since it does not have this property. 22 | // However, the userRequest property can supplement rawRequest in this situation 23 | relative = rawRequest || request 24 | } 25 | 26 | // Strip useless helper folder structure 27 | SKIP_FOLDERS.forEach((filter) => { 28 | relative = relative.replace(new RegExp("(^|/|\\\\)" + filter + "($|/|\\\\)"), (match, group1) => group1) 29 | }) 30 | 31 | // Strip all script file extensions 32 | const fileExt = path.parse(relative).ext 33 | if (SCRIPT_EXTENSIONS.has(fileExt)) { 34 | relative = relative.replace(new RegExp(`${fileExt}$`), "") 35 | } 36 | 37 | let hash = getHashDigest(cleanRequest, HASH_TYPE, DIGEST_TYPE, DIGEST_LENGTH) 38 | let base = path.basename(relative) 39 | 40 | const result = `${base}-${hash}` 41 | 42 | return result 43 | } 44 | 45 | function getFirstModule(iterable) { 46 | for (const entry of iterable) { 47 | return entry 48 | } 49 | } 50 | 51 | export default class ChunkNames 52 | { 53 | constructor({ debug = false }) { 54 | this.debug = debug 55 | } 56 | 57 | apply(compiler) 58 | { 59 | const debug = this.debug 60 | 61 | compiler.plugin("compilation", (compilation) => { 62 | compilation.plugin("optimize", () => { 63 | compilation.chunks.forEach((chunk) => { 64 | const firstModule = getFirstModule(chunk.modulesIterable) 65 | if (firstModule) { 66 | const userRequest = firstModule.userRequest 67 | const rawRequest = firstModule.rawRequest 68 | const oldName = chunk.name 69 | if (userRequest && oldName == null) { 70 | chunk.name = generateChunkName(userRequest, rawRequest) 71 | if (debug) { 72 | console.log("Assigned ChunkName:", chunk.name) 73 | } 74 | } 75 | } 76 | }) 77 | }) 78 | }) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /packages/edge-postcss/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "edge-postcss", 3 | "version": "1.0.0-alpha.14", 4 | "description": "Edge PostCSS is a collection of carefully chosen and configured PostCSS plugins. Part of the Edge Platform.", 5 | "main": "postcss.config.js", 6 | "scripts": { 7 | "build": "rimraf lib && prepublish", 8 | "prepack": "npm run build", 9 | "test": "jest" 10 | }, 11 | "engines": { 12 | "node": ">=6.0.0", 13 | "yarn": ">=1.0.0", 14 | "npm": ">=4.0.0" 15 | }, 16 | "keywords": [], 17 | "files": [ 18 | "bin/", 19 | "docs/", 20 | "lib/" 21 | ], 22 | "author": { 23 | "name": "Sebastian Software", 24 | "email": "s.werner@sebastian-software.de", 25 | "url": "https://www.sebastian-software.de" 26 | }, 27 | "license": "Apache-2.0", 28 | "jest": { 29 | "testPathIgnorePatterns": [ 30 | "core.js" 31 | ] 32 | }, 33 | "dependencies": { 34 | "app-root-dir": "^1.0.2", 35 | "autoprefixer": "^9.1.5", 36 | "lost": "^8.3.0", 37 | "pleeease-filters": "^4.0.0", 38 | "postcss": "^7.0.2", 39 | "postcss-advanced-variables": "^2.3.3", 40 | "postcss-assets": "^5.0.0", 41 | "postcss-at-warn": "^1.0.0", 42 | "postcss-better-colors": "^1.2.2", 43 | "postcss-calc": "^6.0.1", 44 | "postcss-clearfix": "^2.0.1", 45 | "postcss-color-function": "^4.0.1", 46 | "postcss-color-hex-alpha": "^5.0.2", 47 | "postcss-color-hsl": "^2.0.0", 48 | "postcss-color-hwb": "^3.0.0", 49 | "postcss-csso": "^3.0.0", 50 | "postcss-custom-media": "^7.0.2", 51 | "postcss-devtools": "^1.1.1", 52 | "postcss-discard-comments": "^4.0.0", 53 | "postcss-easing-gradients": "^3.0.1", 54 | "postcss-easings": "^2.0.0", 55 | "postcss-flexbugs-fixes": "^4.1.0", 56 | "postcss-flow-root": "0.0.3", 57 | "postcss-font-family-system-ui": "^4.1.0", 58 | "postcss-font-variant": "^4.0.0", 59 | "postcss-gradient-transparency-fix": "^3.0.0", 60 | "postcss-grid-kiss": "^2.1.0", 61 | "postcss-hexrgba": "^1.0.1", 62 | "postcss-import": "^12.0.0", 63 | "postcss-initial": "^3.0.0", 64 | "postcss-input-style": "^1.0.0", 65 | "postcss-magic-animations": "^0.3.0", 66 | "postcss-map": "^0.10.0", 67 | "postcss-media-minmax": "^4.0.0", 68 | "postcss-nested": "^4.1.0", 69 | "postcss-normalize": "^7.0.1", 70 | "postcss-pseudoelements": "^5.0.0", 71 | "postcss-responsive-type": "^1.0.0", 72 | "postcss-sassy-mixins": "^2.1.0", 73 | "postcss-selector-matches": "^4.0.0", 74 | "postcss-smart-asset": "^0.7.4", 75 | "postcss-transform-shortcut": "^2.0.1", 76 | "postcss-will-change": "^3.0.0", 77 | "postcss-zindex": "^4.0.0" 78 | }, 79 | "devDependencies": { 80 | "babel-preset-edge": "4.13.1", 81 | "jest": "23.6.0", 82 | "postcss-load-config": "^2.0.0", 83 | "prepublish": "2.2.0", 84 | "rimraf": "*" 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /packages/edge-builder/src/webpack/dev.js: -------------------------------------------------------------------------------- 1 | import formatWebpackMessages from "react-dev-utils/formatWebpackMessages" 2 | import webpack from "webpack" 3 | import webpackDevMiddleware from "webpack-dev-middleware" 4 | import webpackHotMiddleware from "webpack-hot-middleware" 5 | import webpackHotServerMiddleware from "webpack-hot-server-middleware" 6 | import { notify } from "edge-common" 7 | import clipboardy from "clipboardy" 8 | 9 | import configBuilder from "../builder" 10 | 11 | export function createMiddleware(config = {}) { 12 | const clientConfig = configBuilder("client", "development", config) 13 | const serverConfig = configBuilder("server", "development", config) 14 | 15 | const multiCompiler = webpack([ clientConfig, serverConfig ]) 16 | const clientCompiler = multiCompiler.compilers[0] 17 | 18 | const devMiddleware = webpackDevMiddleware(multiCompiler, { 19 | // required 20 | publicPath: config.output.public, 21 | 22 | // we have our custom error handling for webpack which offers far better DX 23 | logLevel: "silent" 24 | }) 25 | 26 | const hotMiddleware = webpackHotMiddleware(clientCompiler) 27 | 28 | // keeps serverRender updated with arg: { clientStats, outputPath } 29 | const hotServerMiddleware = webpackHotServerMiddleware(multiCompiler, { 30 | serverRendererOptions: { 31 | outputPath: config.output.client 32 | } 33 | }) 34 | 35 | return { 36 | middleware: [ devMiddleware, hotMiddleware, hotServerMiddleware ], 37 | multiCompiler 38 | } 39 | } 40 | 41 | export function connectWithWebpack(server, multiCompiler) { 42 | let serverIsStarted = false 43 | 44 | multiCompiler.plugin("invalid", () => { 45 | notify("Compiling...", "info") 46 | }) 47 | 48 | multiCompiler.plugin("done", (stats) => { 49 | /* eslint-disable no-console */ 50 | const rawMessages = stats.toJson({}) 51 | const messages = formatWebpackMessages(rawMessages) 52 | 53 | const isSuccessful = !messages.errors.length && !messages.warnings.length 54 | if (isSuccessful) { 55 | notify("Compiled successfully!", "info") 56 | } 57 | 58 | // If errors exist, only show errors. 59 | if (messages.errors.length) { 60 | notify("Failed to compile!", "error") 61 | console.log(messages.errors.join("\n\n")) 62 | return 63 | } 64 | 65 | // Show warnings if no errors were found. 66 | if (messages.warnings.length) { 67 | notify("Compiled with warnings!", "warn") 68 | console.log(messages.warnings.join("\n\n")) 69 | } 70 | 71 | if (!stats.hasErrors() && !serverIsStarted) { 72 | serverIsStarted = true 73 | 74 | server.listen(process.env.SERVER_PORT, () => { 75 | notify(`Server started at port ${process.env.SERVER_PORT}`, "info") 76 | 77 | clipboardy 78 | .write(`http://localhost:${process.env.SERVER_PORT}`) 79 | .catch((error) => { 80 | // noop 81 | }) 82 | }) 83 | } 84 | }) 85 | } 86 | -------------------------------------------------------------------------------- /packages/edge-boilerplate/src/Application.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import loadable from "loadable-components" 3 | import { Router } from "@reach/router" 4 | import { IntlProvider } from "react-intl" 5 | import classnames from "classnames/bind" 6 | 7 | import Styles from "./Application.css" 8 | import ViewWrapper from "./components/view/ViewWrapper" 9 | 10 | import HtmlHead from "./components/htmlhead/HtmlHead" 11 | import Navigation from "./components/navigation/Navigation" 12 | 13 | const Classes = classnames.bind(Styles) 14 | 15 | /* eslint-disable no-console */ 16 | 17 | function renderIntlProvider(props) { 18 | const { Component, loading, error, ownProps } = props 19 | const { children, locale } = ownProps 20 | 21 | return ( 22 | 23 | {children} 24 | 25 | 26 | ) 27 | } 28 | 29 | const messages = { 30 | de: loadable(() => import("./messages/de.json"), { render: renderIntlProvider }), 31 | en: loadable(() => import("./messages/en.json"), { render: renderIntlProvider }), 32 | fr: loadable(() => import("./messages/fr.json"), { render: renderIntlProvider }), 33 | es: loadable(() => import("./messages/es.json"), { render: renderIntlProvider }) 34 | } 35 | 36 | function IntlWrapper({ locale, children }) { 37 | const MessagesWrapper = messages[locale] 38 | return ( 39 | {children} 40 | ) 41 | } 42 | 43 | 44 | 45 | const HomeView = loadable( 46 | () => import("./views/Home/Home"), 47 | { render: ViewWrapper } 48 | ) 49 | 50 | const CounterView = loadable( 51 | () => import("./views/Counter/Counter"), 52 | { render: ViewWrapper } 53 | ) 54 | 55 | const LocalizationView = loadable( 56 | () => import("./views/Localization/Localization"), 57 | { render: ViewWrapper } 58 | ) 59 | 60 | const MissingView = loadable( 61 | () => import("./views/Missing/Missing"), 62 | { render: ViewWrapper } 63 | ) 64 | 65 | export function prepare(kernel) { 66 | return Promise.all([ 67 | ]) 68 | } 69 | 70 | /* eslint-disable react/prefer-stateless-function */ 71 | class Application extends React.Component { 72 | state = { 73 | alive: false 74 | } 75 | 76 | componentDidMount() { 77 | requestAnimationFrame(() => { 78 | this.setState({ 79 | alive: true 80 | }) 81 | }) 82 | } 83 | 84 | render() { 85 | return ( 86 | 87 |
88 | 89 | 90 | 91 |
92 | 93 | 94 | 95 | 96 | 97 | 98 |
99 |
100 |
101 | ) 102 | } 103 | } 104 | 105 | export default Application 106 | -------------------------------------------------------------------------------- /packages/edge-builder/src/binary.js: -------------------------------------------------------------------------------- 1 | import meow from "meow" 2 | import chalk from "chalk" 3 | import updateNotifier from "update-notifier" 4 | import Promise from "bluebird" 5 | import clearConsole from "react-dev-utils/clearConsole" 6 | 7 | import { loadConfig, NAME, VERSION } from "edge-common" 8 | import { 9 | buildClient, 10 | buildServer, 11 | cleanClient, 12 | cleanServer 13 | } from "./commands/build" 14 | 15 | import pkg from "../package.json" 16 | 17 | const IS_INTERACTIVE = process.stdout.isTTY 18 | 19 | if (IS_INTERACTIVE) { 20 | clearConsole() 21 | } 22 | 23 | console.log( 24 | `${chalk.bold( 25 | `EDGE ${chalk.green(`v${pkg.version}`)}` 26 | )} running on ${chalk.bold.blue(NAME)}-${VERSION}` 27 | ) 28 | 29 | // Parse arguments 30 | const command = meow( 31 | ` 32 | Usage: 33 | $ edge 34 | 35 | Options: 36 | --config Path to configuration file. 37 | --verbose, -v Generate verbose output messages. 38 | --quiet, -q Reduce amount of output messages to warnings and errors. 39 | 40 | Commands: 41 | build Build production appliction 42 | build:client Build client part of production application 43 | build:server Build server part of production application 44 | clean Clean up all generated files 45 | `, 46 | { 47 | flags: { 48 | verbose: { 49 | alias: "v", 50 | default: false 51 | }, 52 | quiet: { 53 | alias: "q", 54 | default: false 55 | } 56 | } 57 | } 58 | ) 59 | 60 | const selectedTasks = command.input 61 | const flags = command.flags 62 | 63 | // Check for updates first 64 | /* eslint-disable no-magic-numbers */ 65 | updateNotifier({ 66 | pkg, 67 | 68 | // check every hour 69 | updateCheckInterval: 1000 * 60 * 60 70 | }).notify() 71 | 72 | // List of tasks we have available 73 | const availableTasks = [ 74 | { task: "clean", commands: [ cleanClient, cleanServer ] }, 75 | { 76 | task: "build", 77 | commands: [ cleanClient, cleanServer, buildClient, buildServer ] 78 | }, 79 | { task: "build:client", commands: [ cleanClient, buildClient ] }, 80 | { task: "build:server", commands: [ cleanServer, buildServer ] } 81 | ] 82 | 83 | // Prevent deprecation messages which should not be displayed to the end user 84 | if (!flags.verbose) { 85 | process.noDeprecation = true 86 | } 87 | 88 | /* eslint-disable no-process-exit, max-depth, no-console */ 89 | 90 | function executeCommands(listOfCommands, config) { 91 | /* eslint-disable no-use-extend-native/no-use-extend-native */ 92 | return Promise.each(listOfCommands, (item) => item(config)) 93 | } 94 | 95 | async function executeTasks() { 96 | const { config } = await loadConfig("edge", flags) 97 | 98 | for (const taskName of selectedTasks) { 99 | for (const taskConfig of availableTasks) { 100 | if (taskConfig.task === taskName) { 101 | try { 102 | await executeCommands(taskConfig.commands, config) 103 | } catch (error) { 104 | console.error(chalk.bold.red(`Failed to execute task: ${taskName}!`)) 105 | console.error(error) 106 | process.exit(1) 107 | } 108 | } 109 | } 110 | } 111 | } 112 | 113 | if (selectedTasks.length > 0) { 114 | process.nextTick(executeTasks) 115 | } else { 116 | command.showHelp() 117 | } 118 | -------------------------------------------------------------------------------- /packages/edge-storybook/readme.md: -------------------------------------------------------------------------------- 1 | # Edge Storybook Config 2 | 3 | This project contains our configuration and the dependencies to simplify usage of Storybook inside applications and components. 4 | 5 | ## Features 6 | 7 | - Babel Preset Edge is built-in so that you are able to use all typical ES features you know from any `edge-builder`-based project. 8 | - Hot Loading to allow straight-forward development of components inside of Storybook. 9 | - Integrated action logger for testing buttons and other interactive components. 10 | - Responsive Panel with device selector for easily accessing common screen resolutions. 11 | - Support for Storyshots to combine your stories with Jest's powerful snapshot testing. 12 | - Webpack has support for CSS Modules and generic URL handling for all typical asset files. 13 | - Wrapped with our typical Intl Provider (Localization) and State Provider (Redux). 14 | 15 | ## Installation / Config 16 | 17 | Install the config using: 18 | 19 | ``` 20 | npm install --save-dev edge-storybook 21 | ``` 22 | 23 | Then add the following scripts to your `package.json`: 24 | 25 | ```json 26 | "storybook": "start-storybook --port 1449 --config-dir node_modules/edge-storybook/lib", 27 | "storybook:build": "build-storybook --output-dir docs/storybook --config-dir node_modules/edge-storybook/lib", 28 | ``` 29 | 30 | We currently default to port `1449` in all our Storybooks, but you can tweak the value as needed. 31 | 32 | ## Create Stories 33 | 34 | Storybook is configured in a way that it automatically adds all stories which match the following pattern: `/\.story\.js$/` e.g. `Button.story.js` 35 | 36 | Stories should be placed inside the component folder for each component and developed alongside e.g.: 37 | 38 | ``` 39 | src/components/button/Button.js 40 | src/components/button/Button.css 41 | src/components/button/Button.story.js 42 | src/components/button/Button.test.js 43 | ``` 44 | 45 | ## Start Storybook 46 | 47 | To run Storybook run the command `npm run storybook`. When configured as listed above you should be able to see your Storybook at `localhost:1449` inside your preferred browser. 48 | 49 | This is also the ideal environment for development of plain, application-unaware components. One major benefit here is that Storybook sandboxes each demonstrated component from the other available components. 50 | 51 | ## Deploy Demo Environment 52 | 53 | After you have executed `npm run storybook:build` you got a new folder, typically `docs/storybook` which is ready to be published on any static HTML server like Nginx. 54 | 55 | ## Snapshot Testing 56 | 57 | As we configured Storybook for being integrated with Jest's so-called snapshot testing it is pretty straightforward to use this inside your project. Typically you add the following file into your components folder under the name `Stories.test.js`: 58 | 59 | ```js 60 | import initStoryshots from "@storybook/addon-storyshots" 61 | 62 | initStoryshots({ 63 | configPath: "./node_modules/edge-storybook/lib" 64 | }) 65 | ``` 66 | 67 | It makes sense to just use shallow snapshots. That's how Jest normally behaves as well with snapshot tests. You typically do not want to include the fully resolved DOM structure into all snapshots. Testing should end at the boundaries of your components. 68 | 69 | 70 | ## License 71 | 72 | [Apache License Version 2.0, January 2004](license) 73 | 74 | 75 | ## Copyright 76 | 77 | Logo of Sebastian Software GmbH, Mainz, Germany 78 | 79 | Copyright 2017-2018
[Sebastian Software GmbH](http://www.sebastian-software.de) 80 | -------------------------------------------------------------------------------- /packages/edge-style/readme.md: -------------------------------------------------------------------------------- 1 | # Edge Style
[![Sponsored by][sponsor-img]][sponsor] [![Version][npm-version-img]][npm] [![Downloads][npm-downloads-img]][npm] [![Build Status Unix][travis-img]][travis] [![Build Status Windows][appveyor-img]][appveyor] [![Dependencies][deps-img]][deps] 2 | 3 | [sponsor-img]: https://img.shields.io/badge/Sponsored%20by-Sebastian%20Software-692446.svg 4 | [sponsor]: https://www.sebastian-software.de 5 | [deps]: https://david-dm.org/sebastian-software/edge-style 6 | [deps-img]: https://david-dm.org/sebastian-software/edge-style.svg 7 | [npm]: https://www.npmjs.com/package/edge-style 8 | [npm-downloads-img]: https://img.shields.io/npm/dm/edge-style.svg 9 | [npm-version-img]: https://img.shields.io/npm/v/edge-style.svg 10 | [travis-img]: https://img.shields.io/travis/sebastian-software/edge-style/master.svg?branch=master&label=unix%20build 11 | [appveyor-img]: https://img.shields.io/appveyor/ci/swernerx/edge-style/master.svg?label=windows%20build 12 | [travis]: https://travis-ci.org/sebastian-software/edge-style 13 | [appveyor]: https://ci.appveyor.com/project/swernerx/edge-style/branch/master 14 | 15 | > The Edge Platform helps you focus on business logic rather than dealing with massive tooling, common patterns, complex configurations. 16 | 17 | We know the hassle of adding basic sensible styling to your Single Page Application. 18 | Interestingly there were a lot of seperate libraries and solutions but not a useful combination of all of it. 19 | Edge Style is our take on delivering same basic styling for SPAs. 20 | 21 | ## Features 22 | 23 | - **Normalize**: Based on normalize.css but using the application's customized browerslist: Why add the full normalize if you can only use what's needed? 24 | - **Reset**: Reset of all margins and paddings on block level elements: Layouting is easier when having a blank slate. 25 | - **Sanitize**: Sanitizes typical gotchas and non ideal legacy standards. 26 | - **Box Sizing**: Configures all elements and their shadow elements to use `border-box`. 27 | - **Aria**: Implements some best practises on ARIA markup. 28 | - **OpenType**: Tweaks default behavior of all native elements to use advanced OpenType features where useful. 29 | - **Intl**: Localized configuration for HTML quotes. 30 | 31 | ## Installation 32 | 33 | ``` 34 | npm install --save-dev edge-style 35 | ``` 36 | 37 | Edge Style requires a PostCSS-based setup where [postcss-normalize](https://github.com/jonathantneal/postcss-normalize) is enabled in the PostCSS configuration. 38 | 39 | Other than that Edge Style works effectively without any further plugins as there is e.g. no selector nesting being used inside the source files. It couldn't hurt to add [autoprefixer](https://github.com/postcss/autoprefixer) though for managing the minimum amount of prefixes required by your [browserslist](http://browserl.ist/). 40 | 41 | ## Usage 42 | 43 | Anywhere in your application code just import the full package. Ideally it should be the first thing to include on the client side. There is no need for integrating it on the server side code (SSR). 44 | 45 | ```js 46 | import "edge-style" 47 | ``` 48 | 49 | ## Related 50 | 51 | - [Edge PostCSS](https://github.com/sebastian-software/edge-postcss) is our very own PostCSS plugin configuration which comes with autoprefixer and postcss-normalize built-in. 52 | 53 | 54 | 55 | ## License 56 | 57 | [Apache License Version 2.0, January 2004](license) 58 | 59 | 60 | ## Copyright 61 | 62 | Logo of Sebastian Software GmbH, Mainz, Germany 63 | 64 | Copyright 2017-2018
[Sebastian Software GmbH](http://www.sebastian-software.de) 65 | -------------------------------------------------------------------------------- /packages/edge-core/src/common/Intl.js: -------------------------------------------------------------------------------- 1 | import areIntlLocalesSupported from "intl-locales-supported" 2 | import { addLocaleData } from "react-intl" 3 | 4 | /* global Intl */ 5 | 6 | const PREFER_NATIVE = true 7 | 8 | // let intlSupportTable 9 | // if (process.env.TARGET === "node") { 10 | // intlSupportTable = require("caniuse-lite").feature( 11 | // require("caniuse-lite/data/features/internationalization.js") 12 | // ) 13 | // } 14 | 15 | export function requiresIntlPolyfill(locale) { 16 | // Determine if the built-in `Intl` has the locale data we need. 17 | if (PREFER_NATIVE && global.Intl && areIntlLocalesSupported([ locale ])) { 18 | return false 19 | } 20 | 21 | // By default Node only ships with basic English locale data. You can however build a 22 | // Node binary with all locale data. We recommend doing this if you control the container 23 | // your Node app runs in, otherwise you'll want to polyfill Intl in Node. 24 | // Via: https://github.com/yahoo/react-intl/wiki#i18n-in-javascript 25 | if (PREFER_NATIVE === false && process.env.TARGET === "node") 26 | { 27 | /* eslint-disable no-console */ 28 | console.warn("Your NodeJS installation does not include full ICU locale data! Fallback to polyfill!") 29 | console.warn("See also: https://github.com/nodejs/node/wiki/Intl") 30 | } 31 | 32 | return true 33 | } 34 | 35 | export function installIntlPolyfill() { 36 | const Polyfill = global.IntlPolyfill 37 | if (!Polyfill) { 38 | console.log("Can't find IntlPolyfill global!") 39 | return 40 | } 41 | 42 | // `Intl` exists, but it doesn't have the data we need, so load the 43 | // polyfill and patch the constructors we need with the polyfill's. 44 | if (global.Intl) { 45 | Intl.NumberFormat = Polyfill.NumberFormat 46 | Intl.DateTimeFormat = Polyfill.DateTimeFormat 47 | } else { 48 | global.Intl = Polyfill 49 | } 50 | } 51 | 52 | export function requiresReactIntl() { 53 | // Locale Data in Node.js: 54 | // When using React Intl in Node.js (same for the Intl.js polyfill), all locale data will be 55 | // loaded into memory. This makes it easier to write a universal/isomorphic React app with 56 | // React Intl since you won't have to worry about dynamically loading locale data on the server. 57 | // Via: https://github.com/yahoo/react-intl/wiki#locale-data-in-nodejs 58 | 59 | // As mentioned above no additional data has to be loaded for NodeJS. We are just resolving 60 | // the Promise in that case. 61 | if (process.env.TARGET === "node") { 62 | return false 63 | } 64 | 65 | return true 66 | } 67 | 68 | export function installReactIntl(response) { 69 | if (process.env.TARGET !== "node") { 70 | // It seems like React Intls data files are not correctly dealt with in recent 71 | // Webpack. There is data under the "default" key but it's not a correct ESM module. 72 | let normalizedResponse = response 73 | if (!(response instanceof Array) && response.default) { 74 | normalizedResponse = response.default 75 | } 76 | 77 | addLocaleData(normalizedResponse) 78 | } 79 | } 80 | 81 | 82 | /** 83 | * Selector for quering the current locale e.g. de-DE, en-US, ... 84 | */ 85 | export function getLocale(state) { 86 | return state.edge.intl.locale 87 | } 88 | 89 | 90 | /** 91 | * Selector for quering the current language e.g. de, en, fr, es, ... 92 | */ 93 | export function getLanguage(state) { 94 | return state.edge.intl.language 95 | } 96 | 97 | 98 | /** 99 | * Selector for quering the current region e.g. DE, BR, PT, ... 100 | */ 101 | export function getRegion(state) { 102 | return state.edge.intl.region 103 | } 104 | -------------------------------------------------------------------------------- /packages/edge-core/src/common/State.js: -------------------------------------------------------------------------------- 1 | import { createStore, combineReducers, applyMiddleware, compose } from "redux" 2 | import thunk from "redux-thunk" 3 | 4 | const composeEnhancers = (process.env.TARGET === "web" && 5 | process.env.NODE_ENV === "development" && 6 | window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose 7 | 8 | /** 9 | * Placeholder for a non active reducer in Redux. 10 | * 11 | * @param previousState {Object} Previous state. 12 | * @param action {string} Action which is being dispatched. 13 | */ 14 | export function emptyReducer(previousState = {}, action) { 15 | return previousState 16 | } 17 | 18 | 19 | /** 20 | * Placeholder for a non active middleware in Redux. 21 | * 22 | * @param store {Object} Store object to work with. 23 | */ 24 | export function emptyMiddleware(store) { 25 | return (next) => { 26 | return (action) => { 27 | return next(action) 28 | } 29 | } 30 | } 31 | 32 | 33 | /** 34 | * Placeholder for a non active enhancer in Redux. 35 | */ 36 | export function emptyEnhancer(param) { 37 | return param 38 | } 39 | 40 | 41 | /** 42 | * Dummy reducer for exporting Edge Platform specific server-side data 43 | * to the client-side application. 44 | */ 45 | export function edgeReducer(previousState = {}, action) { 46 | return previousState 47 | } 48 | 49 | 50 | 51 | 52 | /** 53 | * Selector for quering the nonce which must be used for injecting script tags. 54 | */ 55 | export function getNonce(state) { 56 | return state.edge.nonce 57 | } 58 | 59 | 60 | /** 61 | * Bundles the given reducers into a root reducer for the application 62 | */ 63 | export function createRootReducer(reducers, router = null) { 64 | const allReducers = { 65 | // Application specific reducers 66 | ...reducers, 67 | 68 | // Edge Platform Data 69 | edge: edgeReducer 70 | } 71 | 72 | // Integration point for Redux First Router 73 | if (router) { 74 | allReducers.location = router.reducer 75 | } 76 | 77 | return combineReducers(allReducers) 78 | } 79 | 80 | 81 | /** 82 | * 83 | * 84 | */ 85 | export function createReduxStore(config = {}) { 86 | const { 87 | reducers = {}, 88 | middlewares = [], 89 | enhancers = [], 90 | state = {}, 91 | router = null 92 | } = config 93 | 94 | const rootReducer = createRootReducer(reducers, router) 95 | 96 | const rootEnhancers = composeEnhancers( 97 | applyMiddleware( 98 | 99 | // Redux middleware that spits an error on you when you try to mutate 100 | // your state either inside a dispatch or between dispatches. 101 | // https://github.com/leoasis/redux-immutable-state-invariant 102 | process.env.NODE_ENV === "development" ? 103 | require("redux-immutable-state-invariant").default() : emptyMiddleware, 104 | 105 | // Basic Promise based async handling 106 | thunk, 107 | 108 | // Redux Router First Middleware 109 | router ? router.middleware : emptyMiddleware, 110 | 111 | // Application specific middlewares 112 | ...middlewares, 113 | 114 | // Add automatic state change logging for client application 115 | // Note: Logger must be the last middleware in chain, otherwise it will log thunk and 116 | // promise, not actual actions (https://github.com/evgenyrodionov/redux-logger/issues/20). 117 | process.env.TARGET === "web" ? 118 | require("redux-logger").createLogger({ collapsed: true }) : emptyMiddleware 119 | ), 120 | 121 | // Redux First Router Enhancer 122 | router ? router.enhancer : emptyEnhancer, 123 | 124 | // Application specific enhancers 125 | ...enhancers 126 | ) 127 | 128 | const store = createStore( 129 | rootReducer, 130 | state, 131 | rootEnhancers 132 | ) 133 | 134 | return store 135 | } 136 | -------------------------------------------------------------------------------- /packages/edge-postcss/__tests__/__snapshots__/extensions.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Clearfix 1`] = ` 4 | ".row:after{ 5 | content:''; 6 | display:block; 7 | clear:both 8 | }" 9 | `; 10 | 11 | exports[`Initial - All 1`] = ` 12 | "ul{ 13 | border-collapse:separate; 14 | border-spacing:0; 15 | caption-side:top; 16 | cursor:auto; 17 | direction:ltr; 18 | empty-cells:show; 19 | font-family:serif; 20 | font-size:medium; 21 | font-style:normal; 22 | -webkit-font-feature-settings:normal; 23 | font-feature-settings:normal; 24 | font-variant:normal; 25 | font-weight:400; 26 | font-stretch:normal; 27 | line-height:normal; 28 | -webkit-hyphens:none; 29 | -ms-hyphens:none; 30 | hyphens:none; 31 | letter-spacing:normal; 32 | list-style:disc outside none; 33 | -moz-tab-size:8; 34 | tab-size:8; 35 | text-align:left; 36 | -moz-text-align-last:auto; 37 | text-align-last:auto; 38 | text-indent:0; 39 | text-shadow:none; 40 | text-transform:none; 41 | visibility:visible; 42 | white-space:normal; 43 | widows:2; 44 | word-spacing:normal; 45 | all:initial 46 | }" 47 | `; 48 | 49 | exports[`Initial 1`] = ` 50 | "h1{ 51 | font-family:initial 52 | }" 53 | `; 54 | 55 | exports[`Normalize.css 1`] = ` 56 | ".before{ 57 | color:#ff4136 58 | } 59 | 60 | 61 | /*! normalize.css v9.0.1 | MIT License | github.com/csstools/normalize.css */ 62 | html{ 63 | line-height:1.15; 64 | -ms-text-size-adjust:100%; 65 | -webkit-text-size-adjust:100% 66 | } 67 | 68 | h1{ 69 | font-size:2em; 70 | margin:.67em 0 71 | } 72 | 73 | hr{ 74 | box-sizing:content-box; 75 | height:0; 76 | overflow:visible 77 | } 78 | 79 | details,main{ 80 | display:block 81 | } 82 | 83 | code,kbd,pre,samp{ 84 | font-family:monospace,monospace; 85 | font-size:1em 86 | } 87 | 88 | abbr[title]{ 89 | -webkit-text-decoration:underline dotted; 90 | text-decoration:underline dotted 91 | } 92 | 93 | b,strong{ 94 | font-weight:bolder 95 | } 96 | 97 | small{ 98 | font-size:80% 99 | } 100 | 101 | svg:not(:root){ 102 | overflow:hidden 103 | } 104 | 105 | button,input,select{ 106 | margin:0 107 | } 108 | 109 | [type=button],[type=reset],[type=submit],button{ 110 | -webkit-appearance:button 111 | } 112 | 113 | fieldset{ 114 | padding:.35em .75em .625em 115 | } 116 | 117 | button,input{ 118 | overflow:visible 119 | } 120 | 121 | legend{ 122 | box-sizing:border-box; 123 | color:inherit; 124 | display:table; 125 | max-width:100%; 126 | white-space:normal 127 | } 128 | 129 | progress{ 130 | display:inline-block; 131 | vertical-align:baseline 132 | } 133 | 134 | button,select{ 135 | text-transform:none 136 | } 137 | 138 | textarea{ 139 | margin:0; 140 | overflow:auto 141 | } 142 | 143 | [type=search]{ 144 | -webkit-appearance:textfield; 145 | outline-offset:-2px 146 | } 147 | 148 | ::-webkit-inner-spin-button,::-webkit-outer-spin-button{ 149 | height:auto 150 | } 151 | 152 | ::-webkit-input-placeholder{ 153 | color:inherit; 154 | opacity:.54 155 | } 156 | 157 | ::-webkit-search-decoration{ 158 | -webkit-appearance:none 159 | } 160 | 161 | ::-webkit-file-upload-button{ 162 | -webkit-appearance:button; 163 | font:inherit 164 | } 165 | 166 | ::-moz-focus-inner{ 167 | border-style:none; 168 | padding:0 169 | } 170 | 171 | :-moz-focusring{ 172 | outline:1px dotted ButtonText 173 | } 174 | 175 | dialog{ 176 | background-color:#fff; 177 | border:solid; 178 | color:#111; 179 | display:block; 180 | height:-moz-fit-content; 181 | height:-webkit-fit-content; 182 | height:fit-content; 183 | left:0; 184 | margin:auto; 185 | padding:1em; 186 | position:absolute; 187 | right:0; 188 | width:-moz-fit-content; 189 | width:-webkit-fit-content; 190 | width:fit-content 191 | } 192 | 193 | dialog:not([open]){ 194 | display:none 195 | } 196 | 197 | summary{ 198 | display:list-item 199 | } 200 | 201 | template{ 202 | display:none 203 | } 204 | 205 | .after{ 206 | color:#2ecc40 207 | }" 208 | `; 209 | 210 | exports[`Responsive Type 1`] = ` 211 | "html{ 212 | font-size:calc(7.60465px + 1.04651vw) 213 | } 214 | 215 | @media screen and (min-width:1280px){ 216 | html{ 217 | font-size:21px 218 | } 219 | } 220 | 221 | @media screen and (max-width:420px){ 222 | html{ 223 | font-size:12px 224 | } 225 | }" 226 | `; 227 | 228 | exports[`System UI 1`] = ` 229 | "body{ 230 | font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue 231 | }" 232 | `; 233 | -------------------------------------------------------------------------------- /packages/edge-express/src/addSecurityMiddleware.js: -------------------------------------------------------------------------------- 1 | import helmet from "helmet" 2 | import parameterProtection from "hpp" 3 | import uuid from "uuid" 4 | 5 | export default function addSecurityMiddleware(server, { enableNonce = true, enableCSP = true }) { 6 | if (enableNonce) { 7 | /* eslint-disable max-params */ 8 | 9 | // Attach a unique "nonce" to every response. This allows use to declare 10 | // inline scripts as being safe for execution against our content security policy. 11 | // @see https://helmetjs.github.io/docs/csp/ 12 | server.use((request, response, next) => { 13 | response.locals.nonce = uuid() 14 | next() 15 | }) 16 | } 17 | 18 | // Don't expose any software information to hackers. 19 | server.disable("x-powered-by") 20 | 21 | // Prevent HTTP Parameter pollution. 22 | server.use(parameterProtection()) 23 | 24 | // Content Security Policy (CSP) 25 | // 26 | // If you are unfamiliar with CSPs then I highly recommend that you do some 27 | // reading on the subject: 28 | // - https://content-security-policy.com/ 29 | // - https://developers.google.com/web/fundamentals/security/csp/ 30 | // - https://developer.mozilla.org/en/docs/Web/Security/CSP 31 | // - https://helmetjs.github.io/docs/csp/ 32 | // 33 | // If you are relying on scripts/styles/assets from other servers (internal or 34 | // external to your company) then you will need to explicitly configure the 35 | // CSP below to allow for this. For example you can see I have had to add 36 | // the polyfill.io CDN in order to allow us to use the polyfill script. 37 | // It can be a pain to manage these, but it's a really great habit to get in 38 | // to. 39 | // 40 | // You may find CSPs annoying at first, but it is a great habit to build. 41 | // The CSP configuration is an optional item for helmet, however you should 42 | // not remove it without making a serious consideration that you do not require 43 | // the added security. 44 | const cspConfig = enableCSP ? { 45 | directives: { 46 | defaultSrc: [ "'self'" ], 47 | 48 | scriptSrc: 49 | [ 50 | // Allow scripts hosted from our application. 51 | "'self'", 52 | 53 | // Note: We will execution of any inline scripts that have the following 54 | // nonce identifier attached to them. 55 | // This is useful for guarding your application whilst allowing an inline 56 | // script to do data store rehydration (redux/mobx/apollo) for example. 57 | // @see https://helmetjs.github.io/docs/csp/ 58 | (request, response) => `'nonce-${response.locals.nonce}'`, 59 | 60 | // Required for eval-source-maps (devtool in webpack) 61 | process.env.NODE_ENV === "development" ? "'unsafe-eval'" : "" 62 | ].filter((value) => value !== ""), 63 | 64 | styleSrc: [ "'self'", "'unsafe-inline'", "blob:" ], 65 | imgSrc: [ "'self'", "data:" ], 66 | fontSrc: [ "'self'", "data:" ], 67 | 68 | // Note: Setting this to stricter than * breaks the service worker. :( 69 | // I can't figure out how to get around this, so if you know of a safer 70 | // implementation that is kinder to service workers please let me know. 71 | // ["'self'", 'ws:'], 72 | connectSrc: [ "*" ], 73 | 74 | // objectSrc: [ "'none'" ], 75 | // mediaSrc: [ "'none'" ], 76 | 77 | childSrc: [ "'self'" ] 78 | } 79 | } : null 80 | 81 | if (enableCSP) { 82 | server.use(helmet.contentSecurityPolicy(cspConfig)) 83 | } 84 | 85 | // The xssFilter middleware sets the X-XSS-Protection header to prevent 86 | // reflected XSS attacks. 87 | // @see https://helmetjs.github.io/docs/xss-filter/ 88 | server.use(helmet.xssFilter()) 89 | 90 | // Frameguard mitigates clickjacking attacks by setting the X-Frame-Options header. 91 | // @see https://helmetjs.github.io/docs/frameguard/ 92 | server.use(helmet.frameguard("deny")) 93 | 94 | // Sets the X-Download-Options to prevent Internet Explorer from executing 95 | // downloads in your site’s context. 96 | // @see https://helmetjs.github.io/docs/ienoopen/ 97 | server.use(helmet.ieNoOpen()) 98 | 99 | // Don’t Sniff Mimetype middleware, noSniff, helps prevent browsers from trying 100 | // to guess (“sniff”) the MIME type, which can have security implications. It 101 | // does this by setting the X-Content-Type-Options header to nosniff. 102 | // @see https://helmetjs.github.io/docs/dont-sniff-mimetype/ 103 | server.use(helmet.noSniff()) 104 | } 105 | -------------------------------------------------------------------------------- /packages/edge-storybook/src/config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */ 2 | import Cookie from "js-cookie" 3 | 4 | // The "global" import fixes issues accessing globals from outside of the VM 5 | // where this script is running. This is mainly relevant for running Storyshots via Jest. 6 | /* eslint-disable no-global-assign, no-native-reassign */ 7 | import global from "global" 8 | import React from "react" 9 | import { addDecorator, configure } from "@storybook/react" 10 | import { addLocaleData, IntlProvider } from "react-intl" 11 | 12 | // Core Reset Styles 13 | import "edge-style" 14 | 15 | // Testing with Storyshots: 16 | // Some components rely on intervals being executed at least once. 17 | // This is also good for test coverage as it hits areas normally not hit when 18 | // intervals are stopped before first execution. 19 | // Note: We can't use Jest's fakeTimers as these snapshots are rendered in a 20 | // different V8 VM where we do not have any access to the Jest API. 21 | if (process.env.NODE_ENV === "test") { 22 | global.setInterval = function fakeSetInterval(callback, timeout) { 23 | callback() 24 | return 0 25 | } 26 | 27 | global.clearInterval = function fakeClearInterval() { 28 | // empty 29 | } 30 | } 31 | 32 | // Activate polyfill for `require.context()` when running inside e.g. Jest/NodeJS 33 | if (process.env.NODE_ENV === "test" && require.context == null) { 34 | /* global __dirname */ 35 | require("babel-plugin-require-context-hook/register")() 36 | require.context = function context(directory, useSubdirectories, regExp) { 37 | return global.__requireContext( 38 | __dirname, 39 | directory, 40 | useSubdirectories, 41 | regExp 42 | ) 43 | } 44 | } 45 | 46 | // Using same cookie as in our applications which should make it possible 47 | // via some UI to switch between different locales. 48 | let locale = 49 | process.env.NODE_ENV === "test" ? 50 | process.env.APP_DEFAULT_LOCALE : 51 | Cookie.get("locale") 52 | 53 | if (locale == null) { 54 | locale = "en-US" 55 | } 56 | 57 | const language = locale.split("-")[0] 58 | 59 | // In tests we keep things static and just use the default locale 60 | const data = require(`react-intl/locale-data/${language}`) 61 | addLocaleData(data) 62 | console.log("React-Intl loaded data for", language) 63 | 64 | // Loading messages for components from application root "messages" folder 65 | // Expect that these messages are named `${language}.json` or `${locale}.json`. 66 | let messageLoader 67 | try { 68 | messageLoader = require.context( 69 | `${process.env.APP_SOURCE}/messages`, 70 | !1, 71 | /\.json$/ 72 | ) 73 | } catch(error) {} 74 | 75 | let messages = {} 76 | 77 | if (messageLoader) { 78 | const localeMatcher = new RegExp(`${locale}\\.json$`) 79 | const languageMatcher = new RegExp(`${language}\\.json$`) 80 | 81 | const localeSpecific = messageLoader 82 | .keys() 83 | .filter((messageFile) => localeMatcher.test(messageFile)) 84 | const languageSpecific = messageLoader 85 | .keys() 86 | .filter((messageFile) => languageMatcher.test(messageFile)) 87 | 88 | const localeData = localeSpecific[0] ? messageLoader(localeSpecific[0]) : {} 89 | const languageData = languageSpecific[0] ? 90 | messageLoader(languageSpecific[0]) : 91 | {} 92 | 93 | // We merge the data from locale and language specific files - if both are avaible. 94 | // Locale specific messages override language only data. 95 | messages = { ...languageData, ...localeData } 96 | } 97 | 98 | // Decorate all stories with localization support so that FormattedMessage, etc. work correctly. 99 | addDecorator((story) => { 100 | return ( 101 | 106 | {story()} 107 | 108 | ) 109 | }) 110 | 111 | // Uses the injected ROOT from our Webpack config to find stories 112 | // relative to the application folder. 113 | 114 | // 1. Require all initializers files e.g. core CSS required for all components, i18n setup, etc. 115 | const initLoader = require.context(process.env.APP_SOURCE, false, /\bInit\.js$/) 116 | const initializers = initLoader.keys().map(initLoader) 117 | 118 | console.log("Loaded", initializers.length, "initializers.") 119 | 120 | // 2. Find and load all stories found somewhere in the application directory. 121 | const storyLoader = require.context( 122 | process.env.APP_SOURCE, 123 | true, 124 | /\.story\.js$/ 125 | ) 126 | const stories = storyLoader.keys().map(storyLoader) 127 | configure(() => stories, module) 128 | 129 | console.log("Added", stories.length, "stories.") 130 | -------------------------------------------------------------------------------- /packages/edge-builder/src/plugins/VerboseProgress.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | 6 | /* eslint-disable no-magic-numbers */ 7 | 8 | import ora from "ora" 9 | import { get as getRoot } from "app-root-dir" 10 | import { relative } from "path" 11 | 12 | const ROOT = getRoot() 13 | 14 | export default class VerboseProgress { 15 | constructor({ prefix }) { 16 | this.prefix = prefix 17 | } 18 | 19 | apply(compiler) { 20 | let doneModules = 0 21 | let spinner = null 22 | let lastModule = null 23 | const prefix = this.prefix ? this.prefix + " " : "" 24 | 25 | function display(message) { 26 | if (message !== "") { 27 | spinner.text = prefix + message 28 | 29 | // We somehow have to force rendering otherwise busy Webpack wouldn't let us. 30 | spinner.render() 31 | } else { 32 | spinner.succeed(prefix + "Done!") 33 | } 34 | } 35 | 36 | function moduleDone(module) { 37 | doneModules += 1 38 | 39 | let humanIndex 40 | let humanModuleId = lastModule 41 | 42 | humanIndex = humanModuleId.lastIndexOf(" ") 43 | humanModuleId = humanIndex === -1 ? humanModuleId : humanModuleId.slice(humanIndex + 1, humanModuleId.length) 44 | 45 | humanIndex = humanModuleId.lastIndexOf("!") 46 | humanModuleId = humanIndex === -1 ? humanModuleId : humanModuleId.slice(humanIndex + 1, humanModuleId.length) 47 | 48 | humanIndex = humanModuleId.indexOf("?") 49 | humanModuleId = humanIndex === -1 ? humanModuleId : humanModuleId.slice(0, humanIndex) 50 | 51 | humanModuleId = relative(ROOT, humanModuleId).replace(/^node_modules\//, "~/") 52 | 53 | if (humanModuleId.startsWith('"') && humanModuleId.endsWith('"')) { 54 | humanModuleId = humanModuleId.slice(1, -1) 55 | } 56 | 57 | // Ignore Context Logic Imports 58 | if (humanModuleId.includes("|")) { 59 | return 60 | } 61 | 62 | // Exclude hard to read directly relative modules 63 | if (humanModuleId.startsWith("..")) { 64 | return 65 | } 66 | 67 | if (/^[a-zA-Z0-9\-_/~\.]{0,50}$/.test(humanModuleId)) { 68 | display(`Building Modules ${humanModuleId}...`) 69 | } 70 | } 71 | 72 | compiler.plugin("compilation", (compilation) => { 73 | if (compilation.compiler.isChild()) { 74 | return 75 | } 76 | 77 | doneModules = 0 78 | 79 | spinner = ora({ interval: 16 }) 80 | spinner.start() 81 | 82 | display(0, "compiling") 83 | 84 | compilation.plugin("build-module", (module) => { 85 | lastModule = module.identifier() 86 | }) 87 | 88 | compilation.plugin("failed-module", moduleDone) 89 | compilation.plugin("succeed-module", moduleDone) 90 | 91 | const syncHooks = { 92 | "seal": "Sealing", 93 | "optimize": "Optimizing", 94 | "optimize-modules-basic": "Optimizing modules", 95 | "optimize-chunks-basic": "Optimizing chunks", 96 | "optimize-chunk-modules": "Optimizing chunk modules", 97 | "optimize-module-order": "Optimizing module order", 98 | "optimize-module-ids": "Optimizing module ids", 99 | "optimize-chunk-order": "Optimizing chunk order", 100 | "optimize-chunk-ids": "Optimizing chunk ids", 101 | "before-hash": "Hashing", 102 | "before-module-assets": "Processing module assets", 103 | "before-chunk-assets": "Processing chunk assets", 104 | "record": "Recording" 105 | } 106 | 107 | Object.keys(syncHooks).forEach((name) => { 108 | let pass = 0 109 | const message = syncHooks[name] 110 | compilation.plugin(name, () => { 111 | if (pass++ > 0) display(message + ` [pass ${pass}]`) 112 | else display(message) 113 | }) 114 | }) 115 | 116 | compilation.plugin("optimize-tree", (chunks, modules, callback) => { 117 | display("Optimizing tree") 118 | callback() 119 | }) 120 | 121 | compilation.plugin("additional-assets", (callback) => { 122 | display("Processing assets") 123 | callback() 124 | }) 125 | 126 | compilation.plugin("optimize-chunk-assets", (chunks, callback) => { 127 | display("Optimizing chunk assets") 128 | callback() 129 | }) 130 | 131 | compilation.plugin("optimize-assets", (assets, callback) => { 132 | display("Optimizing assets") 133 | callback() 134 | }) 135 | }) 136 | 137 | compiler.plugin("emit", (compilation, callback) => { 138 | display("Emitting") 139 | callback() 140 | }) 141 | 142 | compiler.plugin("done", () => { 143 | display("") 144 | }) 145 | } 146 | } 147 | --------------------------------------------------------------------------------