Skip to content

Commit

Permalink
👽 update webpack config and react page demo.
Browse files Browse the repository at this point in the history
  • Loading branch information
weizhiqimail committed Feb 26, 2021
1 parent b72042e commit ecf7b47
Show file tree
Hide file tree
Showing 19 changed files with 379 additions and 27 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ module.exports = {
'semi': ['error', 'always'],
'no-mixed-spaces-and-tabs': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
}
};
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"autoprefixer": "^10.2.4",
"babel-loader": "^8.2.2",
"babel-plugin-import": "^1.13.3",
"case-sensitive-paths-webpack-plugin": "^2.4.0",
"chalk": "^4.1.0",
"clean-webpack-plugin": "^3.0.0",
"cross-env": "^7.0.3",
Expand All @@ -59,6 +60,7 @@
"eslint-plugin-promise": "^4.3.1",
"eslint-plugin-react": "^7.22.0",
"eslint-plugin-standard": "^5.0.0",
"find-up": "^5.0.0",
"html-webpack-plugin": "^5.2.0",
"less": "^4.1.1",
"less-loader": "^8.0.0",
Expand Down
15 changes: 8 additions & 7 deletions scripts/helper.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
const fs = require('fs');
const path = require('path');
const os = require('os');
const dotenv = require('dotenv');
const paths = require('./paths');

const NODE_ENV = process.env.NODE_ENV;
const pkg = require(paths.appPackageJson);

const rootPath = path.resolve(__dirname, '..');
const NODE_ENV = process.env.NODE_ENV;

function getProcessEnv() {
const REACT_APP_REGEXP = /^REACT_APP_/i;
Expand All @@ -17,10 +17,10 @@ function getProcessEnv() {
const result = {};

const dotenvFiles = [
`${rootPath}/.env`,
`${rootPath}/.env.local`,
`${rootPath}/.env.${NODE_ENV}.local`,
`${rootPath}/.env.${NODE_ENV}`,
`${paths.appPath}/.env`,
`${paths.appPath}/.env.local`,
`${paths.appPath}/.env.${NODE_ENV}.local`,
`${paths.appPath}/.env.${NODE_ENV}`,
].filter(Boolean);

dotenvFiles.forEach(dotenvFile => {
Expand All @@ -35,6 +35,7 @@ function getProcessEnv() {
});

result.NODE_ENV = NODE_ENV;
result.packageName = pkg.name;

return result;
}
Expand Down
68 changes: 68 additions & 0 deletions scripts/paths.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const path = require('path');
const fs = require('fs');

// 根目录
const appDirectory = fs.realpathSync(process.cwd());

// 依据根目录,找到相对文件或相对目录
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);

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

const resolveModule = (resolveFn, filePath) => {
const extension = moduleFileExtensions.find(extension => {
return fs.existsSync(resolveFn(`${filePath}.${extension}`))
}
);

if (extension) {
return resolveFn(`${filePath}.${extension}`);
}

return resolveFn(`${filePath}.js`);
};

module.exports = {
// 解析 env 环境变量
dotenv: resolveApp('.env'),
// 项目根目录
appPath: resolveApp('.'),
// 项目打包的目录
appBuild: resolveApp('build'),
// public 资源目录
appPublic: resolveApp('public'),
// public 目录下的 index.html 文件
appHtml: resolveApp('public/index.html'),
// 解析入口文件,入口文件可能是 index.js, index.jsx, index.ts, index.tsx
appIndexJs: resolveModule(resolveApp, 'src/index'),
// package.json 的路径
appPackageJson: resolveApp('package.json'),
// src 目录
appSrc: resolveApp('src'),
// tsconfig 的路径
appTsConfig: resolveApp('tsconfig.json'),
// jsconfig 的路径
appJsConfig: resolveApp('jsconfig.json'),
// yarn.lock 文件的路径
yarnLockFile: resolveApp('yarn.lock'),
// setupTests 文件的路径
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
// setupProxy 文件的路径
proxySetup: resolveApp('src/setupProxy.js'),
// node_modules 的目录路径
appNodeModules: resolveApp('node_modules'),
// service-worker 文件的路径
swSrc: resolveModule(resolveApp, 'src/service-worker'),
};
146 changes: 146 additions & 0 deletions scripts/plugins/ModuleNotFoundPlugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

const chalk = require('chalk');
const findUp = require('find-up');
const path = require('path');

class ModuleNotFoundPlugin {
constructor(appPath, yarnLockFile) {
this.appPath = appPath;
this.yarnLockFile = yarnLockFile;

this.useYarnCommand = this.useYarnCommand.bind(this);
this.getRelativePath = this.getRelativePath.bind(this);
this.prettierError = this.prettierError.bind(this);
}

useYarnCommand() {
try {
return findUp.sync('yarn.lock', { cwd: this.appPath }) != null;
} catch (_) {
return false;
}
}

getRelativePath(_file) {
let file = path.relative(this.appPath, _file);
if (file.startsWith('..')) {
file = _file;
} else if (!file.startsWith('.')) {
file = '.' + path.sep + file;
}
return file;
}

prettierError(err) {
let { details: _details = '', origin } = err;

if (origin == null) {
const caseSensitivity =
err.message &&
/\[CaseSensitivePathsPlugin\] `(.*?)` .* `(.*?)`/.exec(err.message);
if (caseSensitivity) {
const [, incorrectPath, actualName] = caseSensitivity;
const actualFile = this.getRelativePath(
path.join(path.dirname(incorrectPath), actualName)
);
const incorrectName = path.basename(incorrectPath);
err.message = `Cannot find file: '${incorrectName}' does not match the corresponding name on disk: '${actualFile}'.`;
}
return err;
}

const file = this.getRelativePath(origin.resource);
let details = _details.split('\n');

const request = /resolve '(.*?)' in '(.*?)'/.exec(details);
if (request) {
const isModule = details[1] && details[1].includes('module');
const isFile = details[1] && details[1].includes('file');

let [, target, context] = request;
context = this.getRelativePath(context);
if (isModule) {
const isYarn = this.useYarnCommand();
details = [
`Cannot find module: '${target}'. Make sure this package is installed.`,
'',
'You can install this package by running: ' +
(isYarn
? chalk.bold(`yarn add ${target}`)
: chalk.bold(`npm install ${target}`)) +
'.',
];
} else if (isFile) {
details = [`Cannot find file '${target}' in '${context}'.`];
} else {
details = [err.message];
}
} else {
details = [err.message];
}
err.message = [file, ...details].join('\n').replace('Error: ', '');

const isModuleScopePluginError =
err.error && err.error.__module_scope_plugin;
if (isModuleScopePluginError) {
err.message = err.message.replace('Module not found: ', '');
}
return err;
}

apply(compiler) {
const { prettierError } = this;
compiler.hooks.make.intercept({
register(tap) {
if (
!(tap.name === 'MultiEntryPlugin' || tap.name === 'SingleEntryPlugin')
) {
return tap;
}
return Object.assign({}, tap, {
fn: (compilation, callback) => {
tap.fn(compilation, (err, ...args) => {
if (err && err.name === 'ModuleNotFoundError') {
err = prettierError(err);
}
callback(err, ...args);
});
},
});
},
});
compiler.hooks.normalModuleFactory.tap('ModuleNotFoundPlugin', nmf => {
nmf.hooks.afterResolve.intercept({
register(tap) {
if (tap.name !== 'CaseSensitivePathsPlugin') {
return tap;
}
return Object.assign({}, tap, {
fn: (compilation, callback) => {
tap.fn(compilation, (err, ...args) => {
if (
err &&
err.message &&
err.message.includes('CaseSensitivePathsPlugin')
) {
err = prettierError(err);
}
callback(err, ...args);
});
},
});
},
});
});
}
}

module.exports = ModuleNotFoundPlugin;
33 changes: 33 additions & 0 deletions scripts/plugins/WatchMissingNodeModulesPlugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

// This webpack plugin ensures `npm install <library>` forces a project rebuild.
// We’re not sure why this isn't webpack's default behavior.
// See https://github.com/facebook/create-react-app/issues/186.

'use strict';

class WatchMissingNodeModulesPlugin {
constructor(nodeModulesPath) {
this.nodeModulesPath = nodeModulesPath;
}

apply(compiler) {
compiler.hooks.emit.tap('WatchMissingNodeModulesPlugin', compilation => {
var missingDeps = Array.from(compilation.missingDependencies);
var nodeModulesPath = this.nodeModulesPath;

// If any missing files are expected to appear in node_modules...
if (missingDeps.some(file => file.includes(nodeModulesPath))) {
// ...tell webpack to watch node_modules recursively until they appear.
compilation.contextDependencies.add(nodeModulesPath);
}
});
}
}

module.exports = WatchMissingNodeModulesPlugin;
16 changes: 10 additions & 6 deletions scripts/webpack.config.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const chalk = require('chalk');
const baseConfig = require('./webpack.config');
const pkg = require('../package.json');
const helper = require('./helper');
const paths = require('./paths');

const defaultConfig = {
port: process.env.REACT_APP_PORT || 18000
Expand All @@ -18,13 +19,15 @@ module.exports = merge(baseConfig, {
devtool: 'source-map',

devServer: {
port: defaultConfig.port,
hot: true,
disableHostCheck: true,
compress: true,
clientLogLevel: 'silent',
contentBase: path.resolve(__dirname, '../public'),
contentBase: paths.appPublic,
watchContentBase: true,
hot: true,
transportMode: 'ws',
injectClient: false,
port: defaultConfig.port,
historyApiFallback: {
index: path.resolve(__dirname, '../public/index.html'),
disableDotRule: true,
},
after(app, server) {
Expand Down Expand Up @@ -68,7 +71,8 @@ module.exports = merge(baseConfig, {
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html'),
title: pkg.name,
})
}),

]

});
18 changes: 14 additions & 4 deletions scripts/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
const path = require('path');
const webpack = require('webpack');

// 大小写敏感
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
// 模块没找到
const ModuleNotFoundPlugin = require('./plugins/ModuleNotFoundPlugin');
// 监控模块缺失
const WatchMissingNodeModulesPlugin = require('./plugins/WatchMissingNodeModulesPlugin');
const helper = require('./helper');
const paths = require('./paths');

const envObj = helper.getProcessEnv();

module.exports = {
entry: path.resolve(__dirname, '../src/index.tsx'),
entry: paths.appIndexJs,

module: {
rules: [
Expand All @@ -25,15 +31,19 @@ module.exports = {

resolve: {
alias: {
'@': path.resolve(__dirname, '../src'),
'~': path.resolve(__dirname, '../node_modules'),
'@': paths.appSrc,
'~': paths.appNodeModules,
},
extensions: ['.ts', '.tsx', '.js', '.jsx', '.json']
},

plugins: [
new webpack.DefinePlugin({'process.env': JSON.stringify(envObj)}),
new webpack.EnvironmentPlugin(envObj),
new CaseSensitivePathsPlugin(),
new ModuleNotFoundPlugin(paths.appPath),
new webpack.HotModuleReplacementPlugin(),
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
],

stats: 'minimal',
Expand Down
Loading

0 comments on commit ecf7b47

Please sign in to comment.