dn-middleware-webpack5
Version:
--- group: middleware name: webpack5 title: Webpack5 ---
231 lines (202 loc) • 7.41 kB
text/typescript
import * as fs from "fs";
import * as path from "path";
import * as Dawn from "@dawnjs/types";
import globby from "globby";
import getPublicUrlOrPath from "react-dev-utils/getPublicUrlOrPath";
import { FileInfo } from "../types";
// import type { Stats } from "webpack/types";
export const getExistFile = ({
cwd,
files,
returnRelative,
}: {
cwd: string;
files: string[];
returnRelative: boolean;
}) => {
for (const file of files) {
const absFilePath = path.join(cwd, file);
if (fs.existsSync(absFilePath)) {
return returnRelative ? file : absFilePath;
}
}
};
// We use `PUBLIC_URL` enviroinfernment variable or "homepage" field to
// "public path" at which the app is served.
// webpack needs to know it to put the right <script> hrefs into HTML even in
// single-page apps that may serve index.html for nested URLs like /todos/42.
// We can't use a relative path in HTML because we don't want to load something
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
export const getPublicPath = (ctx: Dawn.Context) =>
ctx.publicPath || ctx.publicPath === ""
? ctx.publicPath
: getPublicUrlOrPath(process.env.NODE_ENV === "development", process.env.PUBLIC_URL, "");
// ./src/foo.js => foo
const getFilenameByPath = (f: string) => path.basename(f).split(".")[0];
// format entry and template
export const formatReglikeObject = (params: Record<string, string> | any[] | string) => {
const paramsMap: Record<string, string> = {};
if (typeof params === "string") {
paramsMap[getFilenameByPath(params)] = params;
} else if (Array.isArray(params)) {
params.forEach((e: any) => {
paramsMap[getFilenameByPath(e)] = e;
});
} else {
Object.assign(paramsMap, params);
}
const nameFileList: FileInfo[] = [];
Object.entries(paramsMap).forEach(([nameExpr, fileExpr]) => {
const files = globby.sync(fileExpr);
files.forEach(file => {
const paths = file.split("/").reverse()?.map(getFilenameByPath);
const name = nameExpr.replace(/\((\d+)\)/g, (_, index) => {
return paths[index];
});
nameFileList.push({ name, file });
});
});
return nameFileList;
};
export function formatNullStringToList<T = string>(params?: T | T[] | null): T[] {
if (!params) return [];
else if (Array.isArray(params)) {
return params;
}
return [params];
}
export function formatWebpackMessages({ warnings, errors }: { warnings?: any[]; errors?: any[] }) {
const result = {
errors: Array.isArray(errors)
? errors?.map(message => (typeof message === "string" ? formatMessage(message) : formatMessageObj(message)))
: [],
warnings: Array.isArray(warnings)
? warnings?.map(message => (typeof message === "string" ? formatMessage(message) : formatMessageObj(message)))
: [],
};
if (result.errors.some(isLikelyASyntaxError)) {
// If there are any syntax errors, show just them.
// This prevents a confusing ESLint parsing error
// preceding a much more useful Babel syntax error.
result.errors = result.errors.filter(isLikelyASyntaxError);
}
return result;
}
/**
* @param {number} size the size in bytes
* @returns {string} the formatted size
*/
export function formatSize(size: number) {
if (typeof size !== "number" || Number.isNaN(size) === true) {
return "unknown size";
}
if (size <= 0) {
return "0 bytes";
}
const abbreviations = ["bytes", "KiB", "MiB", "GiB"];
const index = Math.floor(Math.log(size) / Math.log(1024));
return `${+(size / 1024 ** index).toPrecision(3)} ${abbreviations[index]}`;
}
export function makeRow(a: string, b: string): string {
return ` ${a}\t ${b}`;
}
// export function printError(stats: Stats, ctx: Dawn.Context) {
// const info = stats.toJson();
// // Compilation errors (missing modules, syntax errors, etc)
// if (stats.hasErrors()) {
// ctx.console.error("error");
// ctx.console.error(info.errors);
// }
// // Compilation warnings
// if (stats.hasWarnings()) {
// ctx.console.error("warning");
// ctx.console.warn(info.warnings);
// }
// }
const friendlySyntaxErrorLabel = "Syntax error:";
function isLikelyASyntaxError(message: string) {
return message.indexOf(friendlySyntaxErrorLabel) !== -1;
}
// Cleans up webpack error messages.
// eslint-disable-next-line no-unused-vars
function formatMessage(message: string) {
if (!message) return "";
let lines = message ? message?.split("\n") : [];
if (lines.length > 2 && lines[1] === "") {
// Remove extra newline.
lines.splice(1, 1);
}
// Remove webpack-specific loader notation from filename.
// Before:
// ./~/css-loader!./~/postcss-loader!./src/App.css
// After:
// ./src/App.css
if (lines[0]?.lastIndexOf("!") !== -1) {
lines[0] = lines[0]?.substr(lines[0]?.lastIndexOf("!") + 1);
}
// Remove unnecessary stack added by `thread-loader`
var threadLoaderIndex = -1;
lines.forEach(function (line, index) {
if (threadLoaderIndex !== -1) {
return;
}
if (line.indexOf("from thread-loader (worker") !== -1) {
threadLoaderIndex = index;
}
});
if (threadLoaderIndex !== -1) {
lines = lines.slice(0, threadLoaderIndex);
}
lines = lines.filter(function (line) {
// Webpack adds a list of entry points to warning messages:
// @ ./src/index.js
// @ multi react-scripts/~/react-dev-utils/webpackHotDevClient.js ...
// It is misleading (and unrelated to the warnings) so we clean it up.
// It is only useful for syntax errors but we have beautiful frames for them.
return line.indexOf(" @ ") !== 0;
});
// line #0 is filename
// line #1 is the main error message
if (!lines[0] || !lines[1]) {
return lines.join("\n");
}
// Cleans up verbose "module not found" messages for files and packages.
if (lines[1].indexOf("Module not found: ") === 0) {
lines = [
lines[0],
// Clean up message because "Module not found: " is descriptive enough.
lines[1]
.replace("Cannot resolve 'file' or 'directory' ", "")
.replace("Cannot resolve module ", "")
.replace("Error: ", "")
.replace("[CaseSensitivePathsPlugin] ", ""),
];
}
// Cleans up syntax error messages.
if (lines[1].indexOf("Module build failed: ") === 0) {
lines[1] = lines[1].replace("Module build failed: SyntaxError:", friendlySyntaxErrorLabel);
}
// Clean up export errors.
// TODO: we should really send a PR to Webpack for this.
const exportError = /\s*(.+?)\s*(")?export '(.+?)' was not found in '(.+?)'/;
if (lines[1].match(exportError)) {
lines[1] = lines[1].replace(exportError, "$1 '$4' does not contain an export named '$3'.");
}
// Reassemble the message.
message = lines.join("\n");
// Internal stacks are generally useless so we strip them... with the
// exception of stacks containing `webpack:` because they're normally
// from user code generated by WebPack. For more information see
// https://github.com/facebook/create-react-app/pull/1050
message = message.replace(/^\s*at\s((?!webpack:).)*:\d+:\d+[\s)]*(\n|$)/gm, ""); // at ... ...:x:y
return message.trim();
}
type ErrObj = {
moduleIdentifier?: string;
moduleName?: string;
loc?: string;
message?: string;
};
function formatMessageObj({ moduleName, loc, message }: ErrObj) {
return moduleName + "(" + loc + ")" + "\n" + message + "\n";
}