packages/cli: replaced serve-plugin by simplified serve command with plain webpack config

This commit is contained in:
Patrik Oldsberg
2020-02-27 23:54:05 +01:00
parent 999cb25414
commit b876df6bee
7 changed files with 506 additions and 660 deletions
+10 -2
View File
@@ -14,9 +14,13 @@
"devDependencies": {
"@spotify/web-scripts": "^6.0.0",
"@types/fs-extra": "^8.1.0",
"@types/html-webpack-plugin": "^3.2.2",
"@types/inquirer": "^6.5.0",
"@types/node": "^13.7.2",
"@types/react-dev-utils": "^9.0.4",
"@types/recursive-readdir": "^2.2.0",
"@types/webpack": "^4.41.7",
"@types/webpack-dev-server": "^3.10.0",
"nodemon": "^2.0.2",
"ts-node": "^8.6.2"
},
@@ -27,12 +31,16 @@
"chokidar": "^3.3.1",
"commander": "^4.1.1",
"dashify": "^2.0.0",
"fork-ts-checker-webpack-plugin": "^4.0.5",
"fs-extra": "^8.1.0",
"handlebars": "^4.7.3",
"html-webpack-plugin": "^3.2.0",
"inquirer": "^7.0.4",
"react-scripts": "^3.4.0",
"recursive-readdir": "^2.2.2",
"replace-in-file": "^5.0.2"
"replace-in-file": "^5.0.2",
"ts-loader": "^6.2.1",
"webpack": "^4.41.6",
"webpack-dev-server": "^3.10.3"
},
"files": [
"templates",
@@ -0,0 +1,106 @@
import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
import ModuleScopePlugin from 'react-dev-utils/ModuleScopePlugin';
import { Paths } from './paths';
// import checkRequiredFiles from 'react-dev-utils/checkRequiredFiles';
// import ModuleNotFoundPlugin from 'react-dev-utils/ModuleNotFoundPlugin';
// import errorOverlayMiddleware from 'react-dev-utils/errorOverlayMiddleware';
// import evalSourceMapMiddleware from 'react-dev-utils/evalSourceMapMiddleware';
// import WatchMissingNodeModulesPlugin from 'react-dev-utils/WatchMissingNodeModulesPlugin';
export function createConfig(paths: Paths): webpack.Configuration {
return {
mode: 'development',
profile: false,
bail: false,
devtool: 'cheap-module-eval-source-map',
entry: [
require.resolve('webpack-dev-server/client') + '?/',
require.resolve('webpack/hot/dev-server'),
paths.appDevEntry,
],
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx'],
plugins: [
new ModuleScopePlugin(
[paths.appSrc, paths.appDev],
[paths.appPackageJson],
),
],
},
module: {
rules: [
{
test: /\.(tsx?|jsx?|mjs)$/,
enforce: 'pre',
include: [paths.appSrc, paths.appDev],
use: {
loader: 'eslint-loader',
options: {
emitWarning: true,
},
},
},
{
test: /\.(tsx?|jsx?|mjs)$/,
include: [paths.appSrc, paths.appDev],
exclude: /node_modules/,
loader: 'ts-loader',
options: {
// disable type checker - handled by ForkTsCheckerWebpackPlugin
transpileOnly: true,
},
},
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.frag/, /\.xml/],
loader: 'url-loader',
include: paths.appAssets,
options: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]',
},
},
{
test: /\.ya?ml$/,
use: 'yml-loader',
},
{
include: /\.(md)$/,
use: 'raw-loader',
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
output: {
publicPath: '/',
filename: 'bundle.js',
},
plugins: [
new HtmlWebpackPlugin({
template: paths.appHtml,
}),
new ForkTsCheckerWebpackPlugin({
async: true,
useTypescriptIncrementalApi: true,
checkSyntacticErrors: true,
tsconfig: paths.appTsConfig,
reportFiles: ['**', '!**/__tests__/**', '!**/?(*.)(spec|test).*'],
}),
new webpack.HotModuleReplacementPlugin(),
],
node: {
module: 'empty',
dgram: 'empty',
dns: 'mock',
fs: 'empty',
http2: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
},
};
}
@@ -0,0 +1,37 @@
import webpack from 'webpack';
import chalk from 'chalk';
import WebpackDevServer from 'webpack-dev-server';
import { getPaths } from './paths';
import { createConfig } from './config';
function startDevServer(options: { host?: string; port?: number }) {
const host = options.host ?? process.env.HOST ?? '0.0.0.0';
const port = options.port ?? (parseInt(process.env.PORT ?? '', 10) || 3000);
const paths = getPaths();
const config = createConfig(paths);
const compiler = webpack(config);
const server = new WebpackDevServer(compiler, {
hot: true,
publicPath: '/',
quiet: true,
host,
port,
});
server.listen(port, host, (err?: Error) => {
if (err) {
console.error(chalk.red(err.message));
process.exit(1);
}
});
}
export default () => {
try {
startDevServer({});
} catch (error) {
console.error(chalk.red(error.message));
process.exit(1);
}
};
@@ -0,0 +1,37 @@
import { resolve as resolvePath } from 'path';
import { existsSync, realpathSync } from 'fs';
export function getPaths() {
const appDir = realpathSync(process.cwd());
const resolveApp = (path: string) => resolvePath(appDir, path);
const resolveOwn = (path: string) => resolvePath(__dirname, '..', path);
const resolveAppModule = (path: string) => {
for (const ext of ['mjs', 'js', 'ts', 'tsx', 'jsx']) {
const filePath = resolveApp(`${path}.${ext}`);
if (existsSync(filePath)) {
return filePath;
}
}
return resolveApp(`${path}.js`);
};
let appHtml = resolveApp('dev/index.html');
if (!existsSync(appHtml)) {
appHtml = resolveOwn('../../templates/serve_index.html');
}
return {
appHtml,
appPath: resolveApp('.'),
appAssets: resolveApp('assets'),
appSrc: resolveApp('src'),
appDev: resolveApp('dev'),
appDevEntry: resolveAppModule('dev/index'),
appTsConfig: resolveApp('tsconfig.json'),
appNodeModules: resolveApp('node_modules'),
appPackageJson: resolveApp('package.json'),
};
}
export type Paths = ReturnType<typeof getPaths>;
@@ -1,157 +0,0 @@
import { resolve as resolvePath } from 'path';
import { existsSync, realpathSync } from 'fs';
const findPaths = () => {
const appDir = realpathSync(process.cwd());
const resolveApp = (path: string) => resolvePath(appDir, path);
const resolveOwn = (path: string) => resolvePath(__dirname, '..', path);
const resolveAppModule = (path: string) => {
for (const ext of ['mjs', 'js', 'ts', 'tsx', 'jsx']) {
const filePath = resolveApp(`${path}.${ext}`);
if (existsSync(filePath)) {
return filePath;
}
}
return resolveApp(`${path}.js`);
};
let appHtml = resolveApp('dev/index.html');
if (!existsSync(appHtml)) {
appHtml = resolveOwn('../templates/serve_index.html');
}
return {
appHtml,
appPath: resolveApp('.'),
appAssets: resolveApp('assets'),
appSrc: [resolveApp('src'), resolveApp('dev')],
appIndexJs: resolveAppModule('dev/index'),
appTsConfig: resolveApp('tsconfig.json'),
appNodeModules: resolveApp('node_modules'),
appPackageJson: resolveApp('package.json'),
publicUrlOrPath: '/',
};
};
/**
* Webpack is a pain, react-scripts almost has what we want, but we need
* a separate entrypoint (dev/index) and would like to not force the plugin to
* have a index html template.
* Reconfiguring react-scripts is a pain, so the code in this function is the
* serve script inside react-scripts, but cleaned based on a couple of assumptions we make.
*/
export default async () => {
process.env.BABEL_ENV = 'development';
// @ts-ignore
process.env.NODE_ENV = 'development';
// Load our own and then override react-scripts paths before loading other modules
const reactScriptsPaths = require('react-scripts/config/paths');
const paths = findPaths();
Object.assign(reactScriptsPaths, paths);
const chalk = require('react-dev-utils/chalk');
const webpack = require('react-scripts/node_modules/webpack');
const WebpackDevServer = require('react-scripts/node_modules/webpack-dev-server');
const clearConsole = require('react-dev-utils/clearConsole');
const {
choosePort,
createCompiler,
prepareUrls,
} = require('react-dev-utils/WebpackDevServerUtils');
const openBrowser = require('react-dev-utils/openBrowser');
const configFactory = require('react-scripts/config/webpack.config');
const createDevServerConfig = require('react-scripts/config/webpackDevServer.config');
const useYarn = true;
const useTypeScript = true;
const isInteractive = process.stdout.isTTY;
const DEFAULT_PORT = parseInt(process.env.PORT ?? '', 10) || 3000;
const HOST = process.env.HOST || '0.0.0.0';
if (process.env.HOST) {
console.log(
chalk.cyan(
`Attempting to bind to HOST environment variable: ${chalk.yellow(
chalk.bold(process.env.HOST),
)}`,
),
);
console.log(
`If this was unintentional, check that you haven't mistakenly set it in your shell.`,
);
console.log(
`Learn more here: ${chalk.yellow('https://bit.ly/CRA-advanced-config')}`,
);
console.log();
}
const port = await choosePort(HOST, DEFAULT_PORT);
if (port == null) {
return;
}
const config = configFactory('development');
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const appName = require(paths.appPackageJson).name;
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
const urls = prepareUrls(
protocol,
HOST,
port,
paths.publicUrlOrPath.slice(0, -1),
);
const devSocket = {
warnings: (warnings: any) =>
devServer.sockWrite(devServer.sockets, 'warnings', warnings),
errors: (errors: any) =>
devServer.sockWrite(devServer.sockets, 'errors', errors),
};
// Create a webpack compiler that is configured with custom messages.
const compiler = createCompiler({
appName,
config,
devSocket,
urls,
useYarn,
useTypeScript,
tscCompileOnError,
webpack,
});
// Serve webpack assets generated by the compiler over a web server.
const serverConfig = createDevServerConfig(undefined, urls.lanUrlForConfig);
const devServer = new WebpackDevServer(compiler, serverConfig);
// Launch WebpackDevServer.
devServer.listen(port, HOST, (err?: Error) => {
if (err) {
return console.log(err);
}
if (isInteractive) {
clearConsole();
}
console.log(chalk.cyan('Starting the development server...\n'));
openBrowser(urls.localUrlForBrowser);
});
const sigHandler = () => {
devServer.close();
process.exit();
};
process.on('SIGINT', sigHandler);
process.on('SIGTERM', sigHandler);
if (isInteractive) {
// Gracefully exit when stdin ends
process.stdin.on('end', function() {
devServer.close();
process.exit();
});
process.stdin.resume();
}
};
+4 -4
View File
@@ -1,7 +1,7 @@
import program from 'commander';
import createPluginCommand from './commands/createPlugin';
import servePlugin from './commands/servePlugin';
import watch from './commands/watch-deps';
import serve from './commands/serve';
process.on('unhandledRejection', err => {
throw err;
@@ -14,9 +14,9 @@ const main = (argv: string[]) => {
.action(createPluginCommand);
program
.command('serve-plugin')
.description('Serves a plugin dev folder')
.action(servePlugin);
.command('serve')
.description('Serves the dev/ folder of a package')
.action(serve);
program
.command('watch-deps')
+312 -497
View File
File diff suppressed because it is too large Load Diff