1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-11 17:53:59 +02:00

Set up webpack configuration for building examples (#1642)

* Set up webpack configuration for building examples

* Configure react-hot-loader in development

* Improve config, set gh-pages to use examples/dist directory

* PR feedback

* Rename App.js in git
This commit is contained in:
Zach Schneider
2018-02-21 20:19:56 -05:00
committed by Ian Storm Taylor
parent 670ef391a8
commit 2ebf3b462b
14 changed files with 3385 additions and 508 deletions

View File

@@ -8,6 +8,19 @@
], ],
plugins: ["external-helpers"], plugins: ["external-helpers"],
"env": { "env": {
"webpack": {
"presets": [
["env", {
"modules": false
}],
"react",
"stage-0"
],
"plugins": [
"transform-runtime",
"react-hot-loader/babel"
]
},
"test": { "test": {
"presets": [ "presets": [
"env", "env",

1
.gitignore vendored
View File

@@ -1,7 +1,6 @@
# Build files. # Build files.
*.js.map *.js.map
dist/ dist/
examples/build.*.js
lib/ lib/
# Temporary files. # Temporary files.

View File

@@ -33,7 +33,7 @@ Then start the watcher and examples server:
yarn start yarn start
``` ```
Now you can open up `http://localhost:8080/dev.html` in your browser and you'll see the examples site. Any changes you make to the source code will be immediately reflected when you refresh the page. You can open the examples URL quickly with: Now you can open up `http://localhost:8080` in your browser and you'll see the examples site. Any changes you make to the source code will be immediately reflected when you refresh the page. You can open the examples URL quickly with:
``` ```
yarn open yarn open

135
examples/app.js Normal file
View File

@@ -0,0 +1,135 @@
import React from 'react'
import { HashRouter, NavLink, Route, Redirect, Switch } from 'react-router-dom'
import CheckLists from './check-lists'
import CodeHighlighting from './code-highlighting'
import Embeds from './embeds'
import Emojis from './emojis'
import ForcedLayout from './forced-layout'
import History from './history'
import HoveringMenu from './hovering-menu'
import HugeDocument from './huge-document'
import Images from './images'
import Links from './links'
import MarkdownPreview from './markdown-preview'
import MarkdownShortcuts from './markdown-shortcuts'
import PasteHtml from './paste-html'
import PlainText from './plain-text'
import Plugins from './plugins'
import RTL from './rtl'
import ReadOnly from './read-only'
import RichText from './rich-text'
import SearchHighlighting from './search-highlighting'
import SyncingOperations from './syncing-operations'
import Tables from './tables'
/**
* Examples.
*
* @type {Array}
*/
const EXAMPLES = [
['Rich Text', RichText, '/rich-text'],
['Plain Text', PlainText, '/plain-text'],
['Hovering Menu', HoveringMenu, '/hovering-menu'],
['Links', Links, '/links'],
['Images', Images, '/images'],
['Embeds', Embeds, '/embeds'],
['Emojis', Emojis, '/emojis'],
['Markdown Preview', MarkdownPreview, '/markdown-preview'],
['Markdown Shortcuts', MarkdownShortcuts, '/markdown-shortcuts'],
['Check Lists', CheckLists, '/check-lists'],
['Code Highlighting', CodeHighlighting, '/code-highlighting'],
['Tables', Tables, '/tables'],
['Paste HTML', PasteHtml, '/paste-html'],
['Search Highlighting', SearchHighlighting, '/search-highlighting'],
['Syncing Operations', SyncingOperations, '/syncing-operations'],
['Read-only', ReadOnly, '/read-only'],
['RTL', RTL, '/rtl'],
['Plugins', Plugins, '/plugins'],
['Forced Layout', ForcedLayout, '/forced-layout'],
['Huge Document', HugeDocument, '/huge-document'],
['History', History, '/history'],
]
/**
* App.
*
* @type {Component}
*/
export default class App extends React.Component {
state = {
error: null,
info: null,
}
componentDidCatch(error, info) {
this.setState({ error, info })
}
render() {
return (
<HashRouter>
<div className="app">
<div className="nav">
<span className="nav-title">Slate Examples</span>
<div className="nav-links">
<a
className="nav-link"
href="https://github.com/ianstormtaylor/slate"
>
GitHub
</a>
<a className="nav-link" href="https://docs.slatejs.org/">
Docs
</a>
</div>
</div>
<div className="tabs">
{EXAMPLES.map(([name, Component, path]) => (
<NavLink
key={path}
to={path}
className="tab"
activeClassName="active"
>
{name}
</NavLink>
))}
</div>
{this.state.error ? this.renderError() : this.renderExample()}
</div>
</HashRouter>
)
}
renderExample() {
return (
<div className="example">
<Switch>
{EXAMPLES.map(([name, Component, path]) => (
<Route key={path} path={path} component={Component} />
))}
<Redirect from="/" to="/rich-text" />
</Switch>
</div>
)
}
renderError() {
return (
<div className="error">
<p>An error was thrown by one of the example's React components!</p>
<pre className="info">
<code>
{this.state.error.stack}
{'\n'}
{this.state.info.componentStack}
</code>
</pre>
</div>
)
}
}

View File

@@ -1,30 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Slate</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:400,400i,700,700i&subset=latin-ext" >
</head>
<body>
<main></main>
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=default,es6,es7"></script>
<script>
/* eslint-disable */
// Dynamically creates the script and link tag and adds the current time
// in ms as a query on the URL. This ensures the script and the link is
// always reloaded. Useful during debugging because we want to see changes
// to the JavaScript and CSS code immediately.
var head = document.getElementsByTagName('head')[0]
var script = document.createElement('script')
var time = new Date().getTime()
script.type = 'text/javascript'
script.src = 'build.dev.js?' + time
head.appendChild(script)
var link = document.createElement('link')
link.rel = 'stylesheet'
link.href = 'index.css?' + time
head.appendChild(link)
</script>
</body>
</html>

View File

@@ -5,8 +5,6 @@ import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import initialValue from './value.json' import initialValue from './value.json'
const root = window.document.querySelector('main')
/** /**
* The menu. * The menu.
* *
@@ -67,6 +65,8 @@ class Menu extends React.Component {
*/ */
render() { render() {
const root = window.document.getElementById('root')
return ReactDOM.createPortal( return ReactDOM.createPortal(
<div className="menu hover-menu" ref={this.props.menuRef}> <div className="menu hover-menu" ref={this.props.menuRef}>
{this.renderMarkButton('bold', 'format_bold')} {this.renderMarkButton('bold', 'format_bold')}

View File

@@ -1,15 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Slate</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:400,400i,700,700i&subset=latin-ext" >
<link rel="stylesheet" href="index.css">
</head>
<body>
<main></main>
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=default,es6,es7"></script>
<script src="build.prod.js"></script>
</body>
</html>

View File

@@ -1,153 +1,29 @@
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import { HashRouter, NavLink, Route, Redirect, Switch } from 'react-router-dom' import { AppContainer } from 'react-hot-loader'
import App from './app'
import CheckLists from './check-lists' import './index.css'
import CodeHighlighting from './code-highlighting'
import Embeds from './embeds'
import Emojis from './emojis'
import ForcedLayout from './forced-layout'
import History from './history'
import HoveringMenu from './hovering-menu'
import HugeDocument from './huge-document'
import Images from './images'
import Links from './links'
import MarkdownPreview from './markdown-preview'
import MarkdownShortcuts from './markdown-shortcuts'
import PasteHtml from './paste-html'
import PlainText from './plain-text'
import Plugins from './plugins'
import RTL from './rtl'
import ReadOnly from './read-only'
import RichText from './rich-text'
import SearchHighlighting from './search-highlighting'
import SyncingOperations from './syncing-operations'
import Tables from './tables'
/**
* Examples.
*
* @type {Array}
*/
const EXAMPLES = [
['Rich Text', RichText, '/rich-text'],
['Plain Text', PlainText, '/plain-text'],
['Hovering Menu', HoveringMenu, '/hovering-menu'],
['Links', Links, '/links'],
['Images', Images, '/images'],
['Embeds', Embeds, '/embeds'],
['Emojis', Emojis, '/emojis'],
['Markdown Preview', MarkdownPreview, '/markdown-preview'],
['Markdown Shortcuts', MarkdownShortcuts, '/markdown-shortcuts'],
['Check Lists', CheckLists, '/check-lists'],
['Code Highlighting', CodeHighlighting, '/code-highlighting'],
['Tables', Tables, '/tables'],
['Paste HTML', PasteHtml, '/paste-html'],
['Search Highlighting', SearchHighlighting, '/search-highlighting'],
['Syncing Operations', SyncingOperations, '/syncing-operations'],
['Read-only', ReadOnly, '/read-only'],
['RTL', RTL, '/rtl'],
['Plugins', Plugins, '/plugins'],
['Forced Layout', ForcedLayout, '/forced-layout'],
['Huge Document', HugeDocument, '/huge-document'],
['History', History, '/history'],
]
/**
* App.
*
* @type {Component}
*/
class App extends React.Component {
state = {
error: null,
info: null,
}
componentDidCatch(error, info) {
this.setState({ error, info })
}
render() {
return (
<div className="app">
<div className="nav">
<span className="nav-title">Slate Examples</span>
<div className="nav-links">
<a
className="nav-link"
href="https://github.com/ianstormtaylor/slate"
>
GitHub
</a>
<a className="nav-link" href="https://docs.slatejs.org/">
Docs
</a>
</div>
</div>
<div className="tabs">
{EXAMPLES.map(([name, Component, path]) => (
<NavLink
key={path}
to={path}
className="tab"
activeClassName="active"
>
{name}
</NavLink>
))}
</div>
{this.state.error ? this.renderError() : this.renderExample()}
</div>
)
}
renderExample() {
return (
<div className="example">
<Switch>
{EXAMPLES.map(([name, Component, path]) => (
<Route key={path} path={path} component={Component} />
))}
<Redirect from="/" to="/rich-text" />
</Switch>
</div>
)
}
renderError() {
return (
<div className="error">
<p>An error was thrown by one of the example's React components!</p>
<pre className="info">
<code>
{this.state.error.stack}
{'\n'}
{this.state.info.componentStack}
</code>
</pre>
</div>
)
}
}
/**
* Router.
*
* @type {Element} router
*/
const router = (
<HashRouter>
<App />
</HashRouter>
)
/** /**
* Mount the router. * Mount the router.
*/ */
const root = document.body.querySelector('main') const root = window.document.createElement('div')
ReactDOM.render(router, root) root.id = 'root'
window.document.body.appendChild(root)
const render = Component => {
ReactDOM.render(
<AppContainer>
<Component />
</AppContainer>,
root
)
}
render(App)
// Webpack Hot Module Replacement API
if (module.hot) {
module.hot.accept('./app', () => render(App))
}

View File

@@ -1,52 +0,0 @@
<html>
<head>
<meta charset="utf-8" />
<title>Content Editable Test</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<main>
<div contenteditable>
<p>
<strong>Some <em>text.</em></strong>
</p>
<p>
<strong>Some <a href="google">text.</a></strong>
</p>
<p>
<strong>Some text.</strong>
</p>
<img src="https://img.washingtonpost.com/wp-apps/imrs.php?src=https://img.washingtonpost.com/news/speaking-of-science/wp-content/uploads/sites/36/2015/10/as12-49-7278-1024x1024.jpg&w=1484" />
<blockquote>
<p>
<strong>Some <a href="google">text.</a></strong>
</p>
<p>
<strong>Some text.</strong>
</p>
</blockquote>
<p></p>
<p></p>
<div contenteditable="false">
<div contenteditable>
<input value="test" />
</div>
<ul>
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
<table border>
<tr>
<td>1</td>
<td>one</td>
</tr>
<tr>
<td>2</td>
<td>two</td>
</tr>
</table>
</div>
</main>
</body>
</html>

View File

@@ -4,11 +4,10 @@
"workspaces": [ "workspaces": [
"packages/*" "packages/*"
], ],
"umd": "./examples/build.dev.js",
"umdMin": "./examples/build.prod.js",
"devDependencies": { "devDependencies": {
"babel-core": "^6.26.0", "babel-core": "^6.26.0",
"babel-eslint": "^8.2.1", "babel-eslint": "^8.2.1",
"babel-loader": "^7.1.2",
"babel-plugin-external-helpers": "^6.22.0", "babel-plugin-external-helpers": "^6.22.0",
"babel-plugin-transform-runtime": "^6.23.0", "babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.6.1", "babel-preset-env": "^1.6.1",
@@ -16,18 +15,23 @@
"babel-preset-stage-0": "^6.24.1", "babel-preset-stage-0": "^6.24.1",
"babel-runtime": "^6.26.0", "babel-runtime": "^6.26.0",
"chalk": "^1.1.3", "chalk": "^1.1.3",
"clean-webpack-plugin": "^0.1.18",
"copy-webpack-plugin": "^4.4.1",
"cross-env": "^5.1.3", "cross-env": "^5.1.3",
"css-loader": "^0.28.9",
"disc": "^1.3.2", "disc": "^1.3.2",
"eslint": "^4.16.0", "eslint": "^4.16.0",
"eslint-config-prettier": "^2.9.0", "eslint-config-prettier": "^2.9.0",
"eslint-plugin-import": "^2.8.0", "eslint-plugin-import": "^2.8.0",
"eslint-plugin-prettier": "^2.5.0", "eslint-plugin-prettier": "^2.5.0",
"eslint-plugin-react": "^7.6.0", "eslint-plugin-react": "^7.6.0",
"extract-text-webpack-plugin": "^3.0.2",
"faker": "^3.1.0", "faker": "^3.1.0",
"fbjs": "^0.8.3", "fbjs": "^0.8.3",
"fs-promise": "^1.0.0", "fs-promise": "^1.0.0",
"gh-pages": "^0.11.0", "gh-pages": "^0.11.0",
"http-server": "^0.9.0", "html-webpack-plugin": "^2.30.1",
"html-webpack-template": "^6.1.0",
"image-extensions": "^1.1.0", "image-extensions": "^1.1.0",
"immutable": "^3.8.1", "immutable": "^3.8.1",
"is-hotkey": "^0.1.1", "is-hotkey": "^0.1.1",
@@ -37,13 +41,14 @@
"lodash": "^4.17.4", "lodash": "^4.17.4",
"matcha": "^0.7.0", "matcha": "^0.7.0",
"mocha": "^2.5.3", "mocha": "^2.5.3",
"npm-run-all": "^4.1.1", "npm-run-all": "^4.1.2",
"prettier": "^1.10.2", "prettier": "^1.10.2",
"prismjs": "^1.5.1", "prismjs": "^1.5.1",
"react": "^16.0.0", "react": "^16.0.0",
"react-dom": "^16.0.0", "react-dom": "^16.0.0",
"react-frame-aware-selection-plugin": "^1.0.0", "react-frame-aware-selection-plugin": "^1.0.0",
"react-frame-component": "^1.1.1", "react-frame-component": "^1.1.1",
"react-hot-loader": "^3.1.3",
"react-portal": "^3.1.0", "react-portal": "^3.1.0",
"react-router": "^2.5.1", "react-router": "^2.5.1",
"react-router-dom": "^4.1.1", "react-router-dom": "^4.1.1",
@@ -64,8 +69,12 @@
"slate-soft-break": "^0.6.0", "slate-soft-break": "^0.6.0",
"slate-sugar": "^0.6.1", "slate-sugar": "^0.6.1",
"source-map-support": "^0.4.0", "source-map-support": "^0.4.0",
"style-loader": "^0.20.2",
"to-camel-case": "^1.0.0", "to-camel-case": "^1.0.0",
"to-title-case": "^1.0.0", "to-title-case": "^1.0.0",
"uglifyjs-webpack-plugin": "^1.1.8",
"webpack": "^3.11.0",
"webpack-dev-server": "^2.11.1",
"yaml-js": "^0.2.0" "yaml-js": "^0.2.0"
}, },
"peerDependencies": { "peerDependencies": {
@@ -80,13 +89,14 @@
"benchmark:save": "mkdir -p ./tmp && cross-env BABEL_ENV=test babel-node ./node_modules/.bin/_matcha --reporter ./support/benchmark/reporter ./packages/*/benchmark/index.js > ./tmp/benchmark-baseline.json", "benchmark:save": "mkdir -p ./tmp && cross-env BABEL_ENV=test babel-node ./node_modules/.bin/_matcha --reporter ./support/benchmark/reporter ./packages/*/benchmark/index.js > ./tmp/benchmark-baseline.json",
"bootstrap": "lerna bootstrap && yarn build", "bootstrap": "lerna bootstrap && yarn build",
"build": "rollup --config", "build": "rollup --config",
"clean": "lerna run clean && rm -rf ./node_modules ./dist ./examples/build.*.js", "build:gh": "cross-env NODE_ENV=production rollup --config && cross-env NODE_ENV=production webpack --config support/webpack.config.js",
"gh-pages": "cross-env NODE_ENV=production yarn build && gh-pages --dist ./examples", "clean": "lerna run clean && rm -rf ./node_modules ./dist ./examples/dist",
"gh-pages": "yarn build:gh && gh-pages --dist ./examples/dist",
"lint": "eslint packages/*/src packages/*/test examples/*/*.js examples/dev/*/*.js && prettier --list-different '**/*.{js,jsx,md,json,css}'", "lint": "eslint packages/*/src packages/*/test examples/*/*.js examples/dev/*/*.js && prettier --list-different '**/*.{js,jsx,md,json,css}'",
"open": "open http://localhost:8080/dev.html", "open": "open http://localhost:8080",
"prettier": "prettier --write '**/*.{js,jsx,md,json,css}'", "prettier": "prettier --write '**/*.{js,jsx,md,json,css}'",
"release": "yarn test && yarn lint && lerna publish && yarn gh-pages", "release": "yarn test && yarn lint && lerna publish && yarn gh-pages",
"server": "http-server ./examples", "server": "webpack-dev-server --config support/webpack.config.js",
"start": "npm-run-all --parallel --print-label watch server", "start": "npm-run-all --parallel --print-label watch server",
"test": "cross-env BABEL_ENV=test mocha --require babel-core/register ./packages/*/test/index.js", "test": "cross-env BABEL_ENV=test mocha --require babel-core/register ./packages/*/test/index.js",
"watch": "rollup --config --watch" "watch": "rollup --config --watch"

View File

@@ -1,5 +1,4 @@
import factory from './support/rollup/packages' import factory from './support/rollup/packages'
import examples from './support/rollup/examples'
import slate from './packages/slate/package.json' import slate from './packages/slate/package.json'
import slateBase64Serializer from './packages/slate-base64-serializer/package.json' import slateBase64Serializer from './packages/slate-base64-serializer/package.json'
import slateDevLogger from './packages/slate-dev-logger/package.json' import slateDevLogger from './packages/slate-dev-logger/package.json'
@@ -22,7 +21,6 @@ const configurations = [
...factory(slateReact), ...factory(slateReact),
...factory(slateSchemaViolations), ...factory(slateSchemaViolations),
...factory(slateSimulator), ...factory(slateSimulator),
...examples,
] ]
export default configurations export default configurations

View File

@@ -1,104 +0,0 @@
import babel from 'rollup-plugin-babel'
import builtins from 'rollup-plugin-node-builtins'
import commonjs from 'rollup-plugin-commonjs'
import globals from 'rollup-plugin-node-globals'
import json from 'rollup-plugin-json'
import replace from 'rollup-plugin-replace'
import resolve from 'rollup-plugin-node-resolve'
import sourcemaps from 'rollup-plugin-sourcemaps'
import uglify from 'rollup-plugin-uglify'
import pkg from '../../package.json'
/**
* Return a Rollup configuration for the examples with `env`.
*
* @param {String} env
* @return {Object}
*/
function configure(env) {
const isDev = env === 'development'
const isProd = env === 'production'
return {
input: 'examples/index.js',
output: {
file: isProd ? pkg.umdMin : pkg.umd,
name: 'slate-examples',
format: 'umd',
exports: 'named',
sourcemap: isDev ? 'inline' : false,
},
watch: {
include: ['examples/**', 'packages/*/lib/*.es.js'],
},
plugins: [
// Allow Rollup to resolve modules from `node_modules`, since it only
// resolves local modules by default.
resolve({
browser: true,
preferBuiltins: true,
}),
// Allow Rollup to resolve CommonJS modules, since it only resolves ES2015
// modules by default.
commonjs({
exclude: ['examples/**'],
// HACK: Sometimes the CommonJS plugin can't identify named exports, so
// we have to manually specify named exports here for them to work.
// https://github.com/rollup/rollup-plugin-commonjs#custom-named-exports
namedExports: {
esrever: ['reverse'],
immutable: [
'List',
'Map',
'Record',
'OrderedSet',
'Set',
'Stack',
'is',
],
'react-dom': ['findDOMNode'],
'react-dom/server': ['renderToStaticMarkup'],
},
}),
// Convert JSON imports to ES6 modules.
json(),
// Replace `process.env.NODE_ENV` with its value, which enables some
// modules like React and Slate to use their production variant.
replace({
'process.env.NODE_ENV': JSON.stringify(env),
}),
// Register Node.js builtins for browserify compatibility.
builtins(),
// Use Babel to transpile the result, limiting it to the source code.
babel({
include: ['examples/**'],
}),
// Register Node.js globals for browserify compatibility.
globals(),
// Only minify the output in production, since it is very slow.
isProd && uglify(),
// Only parse sourcemaps of dependencies in development.
isDev && sourcemaps(),
].filter(Boolean),
}
}
/**
* Export.
*
* @type {Array}
*/
export default [
configure('development'),
process.env.NODE_ENV === 'production' && configure('production'),
].filter(Boolean)

82
support/webpack.config.js Normal file
View File

@@ -0,0 +1,82 @@
const path = require('path')
const webpack = require('webpack')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const HtmlWebpackTemplate = require('html-webpack-template')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const UglifyJSPlugin = require('uglifyjs-webpack-plugin')
const config = {
entry: ['react-hot-loader/patch', './examples/index.js'],
output: {
path: path.resolve(__dirname, 'examples/dist'),
filename: '[name].[hash].js',
},
devServer: {
contentBase: './examples',
publicPath: '/',
hot: true,
},
module: {
rules: [
{
test: /\.js?$/,
use: {
loader: 'babel-loader',
options: {
forceEnv: 'webpack',
},
},
exclude: /node_modules/,
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
sourceMap: true,
},
},
],
}),
},
],
},
plugins: [
new ExtractTextPlugin('[name]-[contenthash].css'),
new HtmlWebpackPlugin({
title: 'Slate',
template: HtmlWebpackTemplate,
inject: false,
links: [
'https://fonts.googleapis.com/css?family=Roboto:400,400i,700,700i&subset=latin-ext',
'https://fonts.googleapis.com/icon?family=Material+Icons',
],
}),
],
}
if (process.env.NODE_ENV === 'production') {
config.plugins.push(
new CleanWebpackPlugin(['examples/dist']),
new UglifyJSPlugin({ sourceMap: true }),
new CopyWebpackPlugin(['examples/CNAME'])
)
config.devtool = 'source-map'
} else {
config.plugins.push(
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
)
config.devtool = 'inline-source-map'
}
module.exports = config

3259
yarn.lock

File diff suppressed because it is too large Load Diff