Skip to content

Commit

Permalink
feat/ssr with ts (#88)
Browse files Browse the repository at this point in the history
* feat: 添加ts example
  • Loading branch information
jerryYuX authored and zhangyuang committed Oct 17, 2019
1 parent e75f3db commit a8ef2b3
Show file tree
Hide file tree
Showing 35 changed files with 1,456 additions and 1 deletion.
17 changes: 17 additions & 0 deletions example/ssr-with-ts/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# http://editorconfig.org
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
max_line_length = off
trim_trailing_whitespace = false

[Makefile]
indent_style = tab
16 changes: 16 additions & 0 deletions example/ssr-with-ts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
logs/
output/
npm-debug.log
yarn-error.log
node_modules/
package-lock.json
yarn.lock
coverage/
dist/
.idea/
run/
.DS_Store
*.sw*
*.un~
.tsbuildinfo
.tsbuildinfo.*
18 changes: 18 additions & 0 deletions example/ssr-with-ts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Egg + React + SSR应用骨架

详细用法实现请查看[官方文档](http://ykfe.net)

# 功能/特性

- [x] 基于cra脚手架开发,由cra开发的React App可无缝迁移,如果你熟悉cra的配置,上手成本几乎为0
- [x] 小而美,相比于beidou,next.js这样的高度封装方案,我们的实现原理和开发模式一目了然
- [x] 同时支持SSR以及CSR两种开发模式,本地开发环境以及线上环境皆可无缝切换两种渲染模式
- [x] 统一前端路由与服务端路由,无需重复编写路由文件配置
- [x] 支持切换路由时自动获取数据
- [x] 支持本地开发HMR
- [x] 稳定性经过线上大规模应用验证,可提供性能优化方案
- [x] 支持tree shaking以及打包去重依赖,使得打包的bundle非常小,为同样复杂度的next.js项目的0.4倍
- [x] 支持csr/ssr自定义layout,无需通过path来手动区分
- [x] 抛弃传统模版引擎,拥抱 React 组件,使用JSX来作为模版
- [ ] 配套[TypeScript](https://github.com/ykfe/egg-react-ssr-typescript)版本的实现
- [ ] 配套serverless版本的实现
93 changes: 93 additions & 0 deletions example/ssr-with-ts/build/env.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
'use strict'

const fs = require('fs')
const path = require('path')
const paths = require('./paths')

// Make sure that including paths.js after env.js will read .env variables.
delete require.cache[require.resolve('./paths')]

const NODE_ENV = process.env.NODE_ENV
if (!NODE_ENV) {
throw new Error(
'The NODE_ENV environment variable is required but was not specified.'
)
}

// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
var dotenvFiles = [
`${paths.dotenv}.${NODE_ENV}.local`,
`${paths.dotenv}.${NODE_ENV}`,
// Don't include `.env.local` for `test` environment
// since normally you expect tests to produce the same
// results for everyone
NODE_ENV !== 'test' && `${paths.dotenv}.local`,
paths.dotenv
].filter(Boolean)

// Load environment variables from .env* files. Suppress warnings using silent
// if this file is missing. dotenv will never modify any environment variables
// that have already been set. Variable expansion is supported in .env files.
// https://github.com/motdotla/dotenv
// https://github.com/motdotla/dotenv-expand
dotenvFiles.forEach(dotenvFile => {
if (fs.existsSync(dotenvFile)) {
require('dotenv-expand')(
require('dotenv').config({
path: dotenvFile
})
)
}
})

// We support resolving modules according to `NODE_PATH`.
// This lets you use absolute paths in imports inside large monorepos:
// https://github.com/facebook/create-react-app/issues/253.
// It works similar to `NODE_PATH` in Node itself:
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
// We also resolve them to make sure all tools using them work consistently.
const appDirectory = fs.realpathSync(process.cwd())
process.env.NODE_PATH = (process.env.NODE_PATH || '')
.split(path.delimiter)
.filter(folder => folder && !path.isAbsolute(folder))
.map(folder => path.resolve(appDirectory, folder))
.join(path.delimiter)

// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
// injected into the application via DefinePlugin in Webpack configuration.
const REACT_APP = /^REACT_APP_/i

function getClientEnvironment (publicUrl) {
const raw = Object.keys(process.env)
.filter(key => REACT_APP.test(key))
.reduce(
(env, key) => {
env[key] = process.env[key]
return env
},
{
// Useful for determining whether we’re running in production mode.
// Most importantly, it switches React into the correct mode.
NODE_ENV: process.env.NODE_ENV || 'development',
// Useful for resolving the correct path to static assets in `public`.
// For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
// This should only be used as an escape hatch. Normally you would put
// images into the `src` and `import` them in code to get their paths.
PUBLIC_URL: publicUrl
}
)
// Stringify all values so we can feed into Webpack DefinePlugin
const stringified = {
'process.env': Object.keys(raw).reduce((env, key) => {
env[key] = JSON.stringify(raw[key])
return env
}, {})
}

return { raw, stringified }
}

module.exports = getClientEnvironment
14 changes: 14 additions & 0 deletions example/ssr-with-ts/build/jest/cssTransform.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use strict'

// This is a custom Jest transformer turning style imports into empty objects.
// http://facebook.github.io/jest/docs/en/webpack.html

module.exports = {
process () {
return 'module.exports = {};'
},
getCacheKey () {
// The output is always the same.
return 'cssTransform'
}
}
30 changes: 30 additions & 0 deletions example/ssr-with-ts/build/jest/fileTransform.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use strict'

const path = require('path')

// This is a custom Jest transformer turning file imports into filenames.
// http://facebook.github.io/jest/docs/en/webpack.html

module.exports = {
process (src, filename) {
const assetFilename = JSON.stringify(path.basename(filename))

if (filename.match(/\.svg$/)) {
return `module.exports = {
__esModule: true,
default: ${assetFilename},
ReactComponent: (props) => ({
$$typeof: Symbol.for('react.element'),
type: 'svg',
ref: null,
key: null,
props: Object.assign({}, props, {
children: ${assetFilename}
})
}),
};`
}

return `module.exports = ${assetFilename};`
}
}
41 changes: 41 additions & 0 deletions example/ssr-with-ts/build/paths.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use strict'

const path = require('path')
const fs = require('fs')

// Make sure any symlinks in the project folder are resolved:
// https://github.com/facebook/create-react-app/issues/637
const appDirectory = fs.realpathSync(__dirname)
const resolveApp = relativePath => path.resolve(appDirectory, relativePath)

const envPublicUrl = process.env.PUBLIC_URL

const getPublicUrl = appPackageJson =>
envPublicUrl || require(appPackageJson).homepage

const moduleFileExtensions = [
'web.mjs',
'mjs',
'web.js',
'js',
'web.ts',
'ts',
'web.tsx',
'tsx',
'json',
'web.jsx',
'jsx'
]

// config after eject: we're in ./config/
module.exports = {
appPath: resolveApp('../'),
appBuild: resolveApp('../output'),
appSrc: resolveApp('../web'),
entry: resolveApp('../web/entry'),
appNodeModules: resolveApp('node_modules'),
publicUrl: getPublicUrl(resolveApp('../package.json')),
resolveApp: resolveApp
}

module.exports.moduleFileExtensions = moduleFileExtensions
46 changes: 46 additions & 0 deletions example/ssr-with-ts/build/util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const paths = require('./paths')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const publicPath = paths.servedPath
const shouldUseRelativeAssetPaths = publicPath === './'
const isDev = process.env.NODE_ENV === 'development'
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
{
loader: MiniCssExtractPlugin.loader,
options: Object.assign(
{},
shouldUseRelativeAssetPaths ? { publicPath: '../../' } : undefined
)
},
{
loader: require.resolve('css-loader'),
options: cssOptions
},
{
loader: require.resolve('postcss-loader'),
options: {
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009'
},
stage: 3
})
]
}
}
]
if (isDev) {
loaders.unshift(require.resolve('css-hot-loader'))
}
if (preProcessor) {
// 添加额外的loader
loaders.push(require.resolve(preProcessor))
}
return loaders
}
module.exports = {
getStyleLoaders
}
115 changes: 115 additions & 0 deletions example/ssr-with-ts/build/webpack.config.base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
'use strict'

const paths = require('./paths')
const path = require('path')
// style files regexes
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent')
const getStyleLoaders = require('./util').getStyleLoaders
const webpackModule = {
strictExportPresence: true,
rules: [
{ parser: { requireEnsure: false } },
{
oneOf: [
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]'
}
},
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
exclude: /node_modules/,
loader: require.resolve('babel-loader'),
options: {
cacheDirectory: true,
cacheCompression: false,
presets: [
[
require.resolve('@babel/preset-env'),
{
modules: 'false'
}
],
['react-app', { 'flow': false, 'typescript': true }]
],
plugins: [
require.resolve('@babel/plugin-transform-runtime')
]
}
},
{
test: /\.css$/,
exclude: /\.module\.css$/,
use: getStyleLoaders({
importLoaders: 1
})
},
{
test: /\.module\.css$/,
use: getStyleLoaders({
importLoaders: 1,
modules: true,
getLocalIdent: getCSSModuleLocalIdent
})
},
{
test: /\.less$/,
exclude: /\.module\.less$/,
use: getStyleLoaders(
{
importLoaders: 2,
localIdentName: '[local]'
},
'less-loader'
),
sideEffects: true
},
{
test: /\.module\.less$/,
use: getStyleLoaders(
{
importLoaders: 2,
modules: true,
getLocalIdent: getCSSModuleLocalIdent
},
'less-loader'
)
},
{
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
loader: require.resolve('file-loader'),
options: {
name: 'static/media/[name].[hash:8].[ext]'
}
}
]
}
]
}

module.exports = {
stats: {
children: false,
entrypoints: false
},
mode: process.env.NODE_ENV,
resolve: {
alias: {
'@': path.resolve(__dirname, '../web')
},
extensions: paths.moduleFileExtensions
.map(ext => `.${ext}`)
},
module: webpackModule,
plugins: [
new MiniCssExtractPlugin({
filename: 'static/css/[name].css',
chunkFilename: 'static/css/[name].chunk.css'
})
],
performance: false
}
Loading

0 comments on commit a8ef2b3

Please sign in to comment.