packages/cli: replaced serve-plugin by simplified serve command with plain webpack config
This commit is contained in:
@@ -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();
|
||||
}
|
||||
};
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user