test #1
2
.eslintignore
Normal file
2
.eslintignore
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
dist
|
||||
@ -1,5 +1,6 @@
|
||||
{
|
||||
"extends": [
|
||||
"ksv741"
|
||||
]
|
||||
"extends": "ksv741",
|
||||
"rules": {
|
||||
"@typescript-eslint/strict-boolean-expressions": "off"
|
||||
}
|
||||
}
|
||||
|
||||
85
README.md
85
README.md
@ -13,10 +13,12 @@
|
||||
- [`Режим запуска`](#режим-запуска)
|
||||
- [`Конфигурация путей`](#конфигурация-путей)
|
||||
- [`Порт`](#порт)
|
||||
- [`Загрузчик`](#загрузчик)
|
||||
- [`Загрузчики`](#загрузчики)
|
||||
- [`Анализ`](#анализ)
|
||||
- [`Плагины`](#плагины)
|
||||
- [`Лоадеры`](#лоадеры)
|
||||
- [`Resolvers`](#resolvers)
|
||||
- [`Кэширование`](#кэширование)
|
||||
- [`Расширенная конфигурация`](#расширенная-конфигурация)
|
||||
|
||||
***
|
||||
@ -127,7 +129,8 @@ root.render(
|
||||
};
|
||||
port?: number;
|
||||
devtool?: string | false;
|
||||
mainLoader?: 'esbuild' | 'swc';
|
||||
mainLoader?: 'esbuild' | 'swc' | 'babel' | 'ts-loader';
|
||||
svgLoader?: 'inline' | 'svgr';
|
||||
analyze?: boolean;
|
||||
plugins?: {
|
||||
htmlWebpackPlugin?: object | 'off';
|
||||
@ -142,16 +145,19 @@ root.render(
|
||||
analyzerPlugin?: object | 'off';
|
||||
}
|
||||
loaders?: {
|
||||
esbuildLoader?: object | 'off';
|
||||
swcLoader?: object | 'off';
|
||||
svgrLoader?: object | 'off';
|
||||
styleLoader?: object | 'off';
|
||||
babelLoader?: object | 'off';
|
||||
cssLoader?: object | 'off';
|
||||
postCssLoader?: object | 'off';
|
||||
miniCssLoader?: object | 'off';
|
||||
sassLoader?: object | 'off';
|
||||
imageLoader?: object | 'off';
|
||||
esbuildLoader?: object | 'off';
|
||||
fontLoader?: object | 'off';
|
||||
imageLoader?: object | 'off';
|
||||
inlineSvgLoader?: object | 'off';
|
||||
miniCssLoader?: object | 'off';
|
||||
postCssLoader?: object | 'off';
|
||||
sassLoader?: object | 'off';
|
||||
styleLoader?: object | 'off';
|
||||
svgrLoader?: object | 'off';
|
||||
swcLoader?: object | 'off';
|
||||
tsLoader?: object | 'off';
|
||||
};
|
||||
}
|
||||
```
|
||||
@ -186,9 +192,11 @@ root.render(
|
||||
- `port` - порт, в котором будет запускаться dev server, для `mode = 'production'` настройка игнорируется
|
||||
По умолчанию: `3000`
|
||||
|
||||
#### Загрузчик
|
||||
- `mainLoader` - главный загрузчик javascript/typescript файлов, доступны `esbuild` и `swc`
|
||||
#### Загрузчики
|
||||
- `mainLoader` - главный загрузчик javascript/typescript файлов, доступны `esbuild`, `swc`, `babel`, `ts-loader`
|
||||
По умолчанию: `esbuild`
|
||||
- `svgLoader` - загрузчик `svg` файлов, доступны `inline` - загрузка файла строкой, `svgr` - загрузка файла как React компонент.
|
||||
По умолчанию: `svgr`
|
||||
|
||||
#### Анализ
|
||||
- `analyze` - запуск сервера на порту `8888`, для анализа сборки. Рекомендуется запускать совместно с `mode = 'production'`. Но допустим запуск и в режиме `development`
|
||||
@ -285,9 +293,12 @@ root.render(
|
||||
- `loaders` - объект с настройками лоадеров
|
||||
|
||||
Список используемых лоадеров:
|
||||
- [esbuild-loader](https://github.com/privatenumber/esbuild-loader) - лоадер для обработки `.tsx`, `.jsx`, `.ts`, `.js` файлов, при указании настройки [`mainLoader: 'esbuild'`](#загрузчик)
|
||||
- [swc-loader](https://github.com/swc-project/pkgs/tree/main/packages/swc-loader) - лоадер для обработки `.tsx`, `.jsx`, `.ts`, `.js` файлов, при указании настройки [`mainLoader: 'swc'`](#загрузчик)
|
||||
- [esbuild-loader](https://github.com/privatenumber/esbuild-loader) - лоадер для обработки `.tsx`, `.jsx`, `.ts`, `.js` файлов, при указании настройки [`mainLoader: 'esbuild'`](#загрузчики)
|
||||
- [swc-loader](https://github.com/swc-project/pkgs/tree/main/packages/swc-loader) - лоадер для обработки `.tsx`, `.jsx`, `.ts`, `.js` файлов, при указании настройки [`mainLoader: 'swc'`](#загрузчики)
|
||||
- [babel-loader](https://github.com/babel/babel-loader) - лоадер для обработки `.tsx`, `.jsx`, `.ts`, `.js` файлов, при указании настройки [`mainLoader: 'babel'`](#загрузчики)
|
||||
- [ts-loader](https://github.com/TypeStrong/ts-loader) - лоадер для обработки `.tsx`, `.jsx`, `.ts`, `.js` файлов, при указании настройки [`mainLoader: 'ts-loader'`](#загрузчики)
|
||||
- [@svgr/webpack](https://github.com/gregberge/svgr/tree/main/packages/webpack)- лоадер для обработки `.svg` файлов, для работы с файлами, как с React компонентами
|
||||
- [svg-inline-loader](https://webpack.js.org/guides/asset-modules/)- лоадер для загрузки `.svg` файлов, для строки
|
||||
- [style-loader](https://webpack.js.org/loaders/style-loader/) - лоадер для вставки стилей inline элементами в html, доступен в режиме `development`
|
||||
- [mini-css-loader](https://webpack.js.org/plugins/mini-css-extract-plugin/#loader-options), доступен в режиме `production` - лоадер для формирование стилей `.css` файлами
|
||||
- [css-loader](https://webpack.js.org/loaders/css-loader/) - лоадер для обработки `.css` файлов
|
||||
@ -304,7 +315,7 @@ root.render(
|
||||
loader: 'tsx',
|
||||
}
|
||||
```
|
||||
- `swcLoader` - настройкаи для [swc-loader](https://swc.rs/docs/configuration/swcrc)
|
||||
- `swcLoader` - настройки для [swc-loader](https://swc.rs/docs/configuration/swcrc)
|
||||
По умолчанию:
|
||||
```js
|
||||
{
|
||||
@ -337,6 +348,18 @@ root.render(
|
||||
},
|
||||
},
|
||||
```
|
||||
- `babelLoader` - настройки для [babel-loader](https://github.com/babel/babel-loader?tab=readme-ov-file#options)
|
||||
По умолчанию:
|
||||
```js
|
||||
{
|
||||
presets: [
|
||||
['@babel/preset-env', { targets: 'defaults' }],
|
||||
['@babel/preset-react', { runtime: 'automatic' }],
|
||||
['@babel/preset-typescript'],
|
||||
],
|
||||
}
|
||||
```
|
||||
- `tsLoader` - настройки для [ts-loader](https://github.com/TypeStrong/ts-loader?tab=readme-ov-file#loader-options)
|
||||
- `svgrLoader` - настройка для [@svgr/webpack](https://react-svgr.com/docs/webpack/#options)
|
||||
По умолчанию:
|
||||
```js
|
||||
@ -344,6 +367,7 @@ root.render(
|
||||
icon: true,
|
||||
}
|
||||
```
|
||||
- `inlineSvgLoader` - настройка для опции `generator` [Assets Modules](https://webpack.js.org/guides/asset-modules/)
|
||||
- `styleLoader` - настройка для [style-loader](https://webpack.js.org/loaders/style-loader/#options)
|
||||
- `miniCssLoader` - настойка для [MiniCssExtractPlugin.loader](https://webpack.js.org/plugins/mini-css-extract-plugin/#loader-options)
|
||||
- `cssLoader` - настройка для [css-loader](https://webpack.js.org/loaders/css-loader/#options)
|
||||
@ -392,6 +416,37 @@ root.render(
|
||||
}
|
||||
```
|
||||
|
||||
### Resolvers
|
||||
- `resolver` - объект с настройками resolvers
|
||||
|
||||
Список используемых плагинов:
|
||||
- [TsconfigPathsPlugin](https://github.com/dividab/tsconfig-paths-webpack-plugin)
|
||||
|
||||
Для настройки плагина необходимо передать объект с конфигурацией или строку `off` для того чтобы отключить плагин:
|
||||
|
||||
- `tsConfigPathsPlugin` - настройки плагина [TsconfigPathsPlugin](https://github.com/dividab/tsconfig-paths-webpack-plugin?tab=readme-ov-file#options)
|
||||
По умолчанию:
|
||||
```javascript
|
||||
resolvers: {
|
||||
plugins: {
|
||||
tsConfigPathsPlugin: {
|
||||
baseUrl: process.cwd()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Кэширование
|
||||
- `cache` - объект с настройками кэширования
|
||||
По умолчанию:
|
||||
```javascript
|
||||
{
|
||||
type: 'filesystem',
|
||||
buildDependencies: {
|
||||
config: [__filename]
|
||||
}
|
||||
}
|
||||
```
|
||||
### Расширенная конфигурация
|
||||
Если текущей кастомизации не достаточно или Вы хотите добавить/изменить какой-то плагин или загрузчик,
|
||||
то это можно легко сделать, т.к. функция `createConfig` возвращает обычный объект с конфигурацией webpack, который можно изменить.
|
||||
|
||||
10
global.d.ts
vendored
10
global.d.ts
vendored
@ -22,6 +22,16 @@ declare module '*.scss' {
|
||||
export = content;
|
||||
}
|
||||
|
||||
declare module '*.css' {
|
||||
const content: Record<string, string>;
|
||||
export = content;
|
||||
}
|
||||
|
||||
declare module '*.sass' {
|
||||
const content: Record<string, string>;
|
||||
export = content;
|
||||
}
|
||||
|
||||
declare module '*.svg' {
|
||||
import type React from 'react';
|
||||
|
||||
|
||||
27
package.json
27
package.json
@ -6,7 +6,8 @@
|
||||
"types": "./dist/index.d.ts",
|
||||
"source": "./src/webpack/index.ts",
|
||||
"scripts": {
|
||||
"build": "rm -rf ./dist && tsc",
|
||||
"prepare": "rm -rf dist && tsc && tsc-alias",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "eslint --ignore-pattern node_modules --color src"
|
||||
},
|
||||
"bin": {
|
||||
@ -32,6 +33,10 @@
|
||||
"author": "Sergey <ksv741@gmail.com> Krylov",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/core": "7.24.0",
|
||||
"@babel/preset-env": "7.24.0",
|
||||
"@babel/preset-react": "7.23.3",
|
||||
"@babel/preset-typescript": "7.23.3",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "0.5.11",
|
||||
"@svgr/webpack": "8.1.0",
|
||||
"@swc/core": "1.4.0",
|
||||
@ -42,6 +47,7 @@
|
||||
"@types/sass-loader": "8.0.8",
|
||||
"@types/webpack-bundle-analyzer": "4.7.0",
|
||||
"@types/webpack-dev-server": "4.7.2",
|
||||
"babel-loader": "9.1.3",
|
||||
"copy-webpack-plugin": "12.0.2",
|
||||
"cross-spawn": "7.0.3",
|
||||
"css-loader": "6.10.0",
|
||||
@ -60,7 +66,10 @@
|
||||
"style-loader": "3.3.4",
|
||||
"swc-loader": "0.2.6",
|
||||
"terser-webpack-plugin": "5.3.10",
|
||||
"ts-loader": "9.5.1",
|
||||
"ts-node": "10.9.2",
|
||||
"tsc-alias": "1.8.8",
|
||||
"tsconfig-paths-webpack-plugin": "4.1.0",
|
||||
"typescript": "5.3.3",
|
||||
"webpack": "5.90.1",
|
||||
"webpack-bundle-analyzer": "4.10.1",
|
||||
@ -68,7 +77,19 @@
|
||||
"webpack-dev-server": "5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "8.56.0",
|
||||
"eslint-config-ksv741": "git+ssh://git@github.com:ksv741/eslint-config-ksv741.git"
|
||||
"eslint": "8.57.0",
|
||||
"eslint-config-ksv741": "0.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">8.0.0",
|
||||
"typescript": "^3.6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
},
|
||||
"eslint": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
25
src/cache/index.ts
vendored
Normal file
25
src/cache/index.ts
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
import type { WebpackOptions } from 'types';
|
||||
import type webpack from 'webpack';
|
||||
|
||||
const getCache = (options: WebpackOptions): webpack.Configuration['cache'] => {
|
||||
const { cache = {} } = options;
|
||||
|
||||
if (cache === 'off') {
|
||||
// eslint-disable-next-line no-undefined
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'filesystem',
|
||||
...cache,
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
buildDependencies: {
|
||||
config: [__filename],
|
||||
// @ts-ignore
|
||||
...cache.buildDependencies,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default getCache;
|
||||
@ -1,24 +1,42 @@
|
||||
import getCache from 'cache';
|
||||
import * as fs from 'fs';
|
||||
import path from 'path';
|
||||
import process from 'process';
|
||||
import type webpack from 'webpack';
|
||||
import { createDevServer } from '../dev-server';
|
||||
import { createLoaders } from '../loaders';
|
||||
import { getOptimization } from '../optimization';
|
||||
import { createOutput } from '../output';
|
||||
import { createPlugins } from '../plugins';
|
||||
import { createResolvers } from '../resolvers';
|
||||
import type { WebpackMode, WebpackOptions, WebpackPaths } from '../types';
|
||||
import createDevServer from 'dev-server';
|
||||
import createLoaders from 'loaders';
|
||||
import getOptimization from 'optimization';
|
||||
import createOutput from 'output';
|
||||
import createPlugins from 'plugins';
|
||||
import createResolvers from 'resolvers';
|
||||
import type { WebpackMode, WebpackOptions, WebpackPaths } from 'types';
|
||||
|
||||
const getDefaultEntryPath = (rootDir: string) => {
|
||||
const getDefaultEntryPath = (rootDir: string, some?: string[] | string): string[] | string => {
|
||||
if (Array.isArray(some)) {
|
||||
// @ts-ignore
|
||||
return some.map((entry) => getDefaultEntryPath(rootDir, entry));
|
||||
}
|
||||
|
||||
if (some && path.isAbsolute(some)) {
|
||||
return some;
|
||||
}
|
||||
|
||||
const definedFile = some && path.resolve(rootDir, some);
|
||||
const indexTsx = path.resolve(rootDir, 'index.tsx');
|
||||
const indexTs = path.resolve(rootDir, 'index.ts');
|
||||
const indexJsx = path.resolve(rootDir, 'index.jsx');
|
||||
const indexJs = path.resolve(rootDir, 'index.js');
|
||||
|
||||
switch (true) {
|
||||
case definedFile && fs.existsSync(definedFile):
|
||||
return definedFile;
|
||||
|
||||
case fs.existsSync(indexTsx):
|
||||
return indexTsx;
|
||||
|
||||
case fs.existsSync(indexTs):
|
||||
return indexTs;
|
||||
|
||||
case fs.existsSync(indexJsx):
|
||||
return indexJsx;
|
||||
|
||||
@ -26,18 +44,53 @@ const getDefaultEntryPath = (rootDir: string) => {
|
||||
return indexJs;
|
||||
|
||||
default:
|
||||
throw new Error(`Has no index.tsx/index.jsx/index.js file in ${rootDir} directory`);
|
||||
throw new Error(`Has no ${definedFile ?? 'index.tsx/index.ts/index.jsx/index.js'} file in ${rootDir} directory`);
|
||||
}
|
||||
};
|
||||
|
||||
const getDefaultPath = ({ root, defaultDirectory, currentPath }: Record<string, string | undefined>) => {
|
||||
if (!currentPath) {
|
||||
// @ts-ignore
|
||||
return path.resolve(root, defaultDirectory);
|
||||
}
|
||||
|
||||
if (path.isAbsolute(currentPath)) {
|
||||
return currentPath;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
return path.resolve(root, currentPath);
|
||||
};
|
||||
|
||||
const getDefaultPaths = (initialPaths?: WebpackPaths): WebpackPaths => {
|
||||
const root = initialPaths?.root ?? path.resolve(process.cwd());
|
||||
const publicPath = initialPaths?.public ?? path.resolve(root, 'public');
|
||||
const src = initialPaths?.src ?? path.resolve(root, 'src');
|
||||
const assets = initialPaths?.assets ?? path.resolve(publicPath, 'assets');
|
||||
const build = initialPaths?.build ?? path.resolve(root, 'build');
|
||||
const entry = getDefaultEntryPath(src);
|
||||
const html = initialPaths?.html ?? path.resolve(publicPath, 'index.html');
|
||||
|
||||
const publicPath = getDefaultPath({
|
||||
root,
|
||||
defaultDirectory: 'public',
|
||||
currentPath: initialPaths?.public,
|
||||
});
|
||||
const src = getDefaultPath({
|
||||
root,
|
||||
defaultDirectory: 'src',
|
||||
currentPath: initialPaths?.src,
|
||||
});
|
||||
const assets = getDefaultPath({
|
||||
root: publicPath,
|
||||
defaultDirectory: 'assets',
|
||||
currentPath: initialPaths?.assets,
|
||||
});
|
||||
const build = getDefaultPath({
|
||||
root,
|
||||
defaultDirectory: 'build',
|
||||
currentPath: initialPaths?.build,
|
||||
});
|
||||
const html = getDefaultPath({
|
||||
root: publicPath,
|
||||
defaultDirectory: 'index.html',
|
||||
currentPath: initialPaths?.html,
|
||||
});
|
||||
const entry = getDefaultEntryPath(src, initialPaths?.entry);
|
||||
|
||||
return {
|
||||
root,
|
||||
@ -60,10 +113,12 @@ const createConfig = (params?: WebpackOptions): webpack.Configuration => {
|
||||
const {
|
||||
mode = getDefaultMode(),
|
||||
analyze = false,
|
||||
mainLoader = 'esbuild',
|
||||
mainLoader,
|
||||
svgLoader,
|
||||
port = 3000,
|
||||
devtool = 'inline-source-map',
|
||||
plugins,
|
||||
loaders,
|
||||
} = params ?? {};
|
||||
const {
|
||||
assets,
|
||||
@ -75,14 +130,11 @@ const createConfig = (params?: WebpackOptions): webpack.Configuration => {
|
||||
src,
|
||||
} = getDefaultPaths(params?.paths);
|
||||
|
||||
if (!fs.existsSync(html)) {
|
||||
throw new Error(`index.html file not found. Define path to index.html file or create one in "${publicPath}" directory`);
|
||||
}
|
||||
|
||||
const options: WebpackOptions = {
|
||||
mode,
|
||||
analyze,
|
||||
mainLoader,
|
||||
svgLoader,
|
||||
port,
|
||||
paths: {
|
||||
assets,
|
||||
@ -94,11 +146,13 @@ const createConfig = (params?: WebpackOptions): webpack.Configuration => {
|
||||
public: publicPath,
|
||||
},
|
||||
plugins,
|
||||
loaders,
|
||||
};
|
||||
|
||||
const isDev = mode === 'development';
|
||||
|
||||
return {
|
||||
context: process.cwd(),
|
||||
mode,
|
||||
entry,
|
||||
output: createOutput(options),
|
||||
@ -111,7 +165,8 @@ const createConfig = (params?: WebpackOptions): webpack.Configuration => {
|
||||
devServer: createDevServer(options),
|
||||
// eslint-disable-next-line no-undefined
|
||||
optimization: !isDev ? getOptimization(options) : undefined,
|
||||
cache: getCache(options),
|
||||
};
|
||||
};
|
||||
|
||||
export { createConfig };
|
||||
export default createConfig;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { Configuration as DevServerConfiguration } from 'webpack-dev-server';
|
||||
import type { WebpackOptions } from '../types';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
export const createDevServer = (options: WebpackOptions): DevServerConfiguration => {
|
||||
const createDevServer = (options: WebpackOptions): DevServerConfiguration => {
|
||||
const {
|
||||
port,
|
||||
} = options;
|
||||
@ -20,3 +20,5 @@ export const createDevServer = (options: WebpackOptions): DevServerConfiguration
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default createDevServer;
|
||||
|
||||
@ -5,4 +5,4 @@ export type {
|
||||
WebpackPaths,
|
||||
WebpackMainLoaders,
|
||||
} from './types';
|
||||
export { createConfig } from './config';
|
||||
export { default as createConfig } from './config';
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type webpack from 'webpack';
|
||||
import type { WebpackOptions } from '../../../types';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
export const getFontLoader = (options: WebpackOptions): webpack.RuleSetRule | null => {
|
||||
const getFontLoader = (options: WebpackOptions): webpack.RuleSetRule | null => {
|
||||
const {
|
||||
loaders: {
|
||||
fontLoader,
|
||||
@ -21,3 +21,5 @@ export const getFontLoader = (options: WebpackOptions): webpack.RuleSetRule | nu
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default getFontLoader;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type webpack from 'webpack';
|
||||
import type { WebpackOptions } from '../../../types';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
export const getImageLoader = (options: WebpackOptions): webpack.RuleSetRule | null => {
|
||||
const getImageLoader = (options: WebpackOptions): webpack.RuleSetRule | null => {
|
||||
const {
|
||||
loaders: {
|
||||
imageLoader,
|
||||
@ -21,3 +21,5 @@ export const getImageLoader = (options: WebpackOptions): webpack.RuleSetRule | n
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default getImageLoader;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type webpack from 'webpack';
|
||||
import type { WebpackOptions } from '../../types';
|
||||
import { getFontLoader } from './font-loader';
|
||||
import { getImageLoader } from './image-loader';
|
||||
import type { WebpackOptions } from 'types';
|
||||
import getFontLoader from './font-loader';
|
||||
import getImageLoader from './image-loader';
|
||||
|
||||
export const getAssetsLoaders = (options: WebpackOptions): (webpack.RuleSetRule | null)[] => [
|
||||
getImageLoader(options),
|
||||
|
||||
@ -1,30 +1,15 @@
|
||||
import type { WebpackOptions } from 'types';
|
||||
import type webpack from 'webpack';
|
||||
import type { WebpackOptions } from '../types';
|
||||
import { getAssetsLoaders } from './asset-loaders';
|
||||
import getMainLoader from './main-loaders';
|
||||
import { getStyleLoaders } from './style-loaders';
|
||||
import { getEsbuildLoader } from './esbuild-loader';
|
||||
import { getSvgLoader } from './svgr-loader';
|
||||
import { getSwcLoader } from './swc-loader';
|
||||
import getSvgLoader from './svg-loaders';
|
||||
|
||||
export const createLoaders = (options: WebpackOptions): (webpack.RuleSetRule | null)[] => {
|
||||
const {
|
||||
mainLoader,
|
||||
} = options;
|
||||
|
||||
const getMainLoader = () => {
|
||||
switch (mainLoader) {
|
||||
case 'swc':
|
||||
return getSwcLoader(options);
|
||||
case 'esbuild':
|
||||
default:
|
||||
return getEsbuildLoader(options);
|
||||
}
|
||||
};
|
||||
|
||||
return [
|
||||
getMainLoader(),
|
||||
const createLoaders = (options: WebpackOptions): (webpack.RuleSetRule | null)[] => [
|
||||
getMainLoader(options),
|
||||
getSvgLoader(options),
|
||||
...getStyleLoaders(options),
|
||||
...getAssetsLoaders(options),
|
||||
].filter(Boolean);
|
||||
};
|
||||
].filter(Boolean);
|
||||
|
||||
export default createLoaders;
|
||||
|
||||
31
src/loaders/main-loaders/babel-loader/index.ts
Normal file
31
src/loaders/main-loaders/babel-loader/index.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import path from 'path';
|
||||
import type webpack from 'webpack';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
const getBabelLoader = (options: WebpackOptions): webpack.RuleSetRule | null => {
|
||||
const {
|
||||
loaders: {
|
||||
babelLoader,
|
||||
} = {},
|
||||
} = options;
|
||||
|
||||
if (babelLoader === 'off') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
test: /\.[j|t]sx?$/,
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
...babelLoader,
|
||||
presets: [
|
||||
['@babel/preset-env', { targets: 'defaults', ...babelLoader?.presets?.env }],
|
||||
['@babel/preset-react', { runtime: 'automatic', ...babelLoader?.presets?.react }],
|
||||
['@babel/preset-typescript', { ...babelLoader?.presets?.typescript }],
|
||||
],
|
||||
},
|
||||
exclude: path.resolve('node_modules'),
|
||||
};
|
||||
};
|
||||
|
||||
export default getBabelLoader;
|
||||
@ -1,8 +1,8 @@
|
||||
import path from 'path';
|
||||
import type webpack from 'webpack';
|
||||
import type { WebpackOptions } from '../../types';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
export const getEsbuildLoader = (options: WebpackOptions): webpack.RuleSetRule | null => {
|
||||
const getEsbuildLoader = (options: WebpackOptions): webpack.RuleSetRule | null => {
|
||||
const {
|
||||
paths,
|
||||
loaders: {
|
||||
@ -19,8 +19,11 @@ export const getEsbuildLoader = (options: WebpackOptions): webpack.RuleSetRule |
|
||||
loader: 'esbuild-loader',
|
||||
options: {
|
||||
loader: 'tsx',
|
||||
jsx: 'automatic',
|
||||
...esbuildLoader,
|
||||
},
|
||||
exclude: path.resolve(paths.root, 'node_modules'),
|
||||
};
|
||||
};
|
||||
|
||||
export default getEsbuildLoader;
|
||||
25
src/loaders/main-loaders/index.ts
Normal file
25
src/loaders/main-loaders/index.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import type { WebpackOptions } from 'types';
|
||||
import getTsLoader from './ts-loader';
|
||||
import getBabelLoader from './babel-loader';
|
||||
import getSwcLoader from './swc-loader';
|
||||
import getEsbuildLoader from './esbuild-loader';
|
||||
|
||||
const getMainLoader = (options: WebpackOptions) => {
|
||||
const {
|
||||
mainLoader = 'esbuild',
|
||||
} = options;
|
||||
|
||||
switch (mainLoader) {
|
||||
case 'babel':
|
||||
return getBabelLoader(options);
|
||||
case 'ts-loader':
|
||||
return getTsLoader(options);
|
||||
case 'swc':
|
||||
return getSwcLoader(options);
|
||||
case 'esbuild':
|
||||
default:
|
||||
return getEsbuildLoader(options);
|
||||
}
|
||||
};
|
||||
|
||||
export default getMainLoader;
|
||||
60
src/loaders/main-loaders/swc-loader/index.ts
Normal file
60
src/loaders/main-loaders/swc-loader/index.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import type webpack from 'webpack';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
const getSwcLoader = (options: WebpackOptions): webpack.RuleSetRule | null => {
|
||||
const {
|
||||
mode,
|
||||
loaders: {
|
||||
swcLoader,
|
||||
} = {},
|
||||
plugins: {
|
||||
reactRefreshPlugin,
|
||||
} = {},
|
||||
} = options;
|
||||
const isDev = mode === 'development';
|
||||
|
||||
if (swcLoader === 'off') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
test: /\.[j|t]sx?$/,
|
||||
exclude: /(node_modules|bower_components)/,
|
||||
loader: 'swc-loader',
|
||||
options: {
|
||||
...swcLoader,
|
||||
sync: true,
|
||||
jsc: {
|
||||
loose: false,
|
||||
externalHelpers: false,
|
||||
keepClassNames: false,
|
||||
...swcLoader?.jsc,
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
tsx: true,
|
||||
dynamicImport: true,
|
||||
privateMethod: true,
|
||||
functionBind: true,
|
||||
exportDefaultFrom: true,
|
||||
exportNamespaceFrom: true,
|
||||
decorators: true,
|
||||
decoratorsBeforeExport: true,
|
||||
topLevelAwait: true,
|
||||
importMeta: true,
|
||||
...swcLoader?.jsc?.parser,
|
||||
},
|
||||
transform: {
|
||||
...swcLoader?.jsc?.transform,
|
||||
react: {
|
||||
runtime: 'automatic',
|
||||
development: isDev,
|
||||
refresh: isDev && reactRefreshPlugin !== 'off',
|
||||
...swcLoader?.jsc?.transform?.react,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default getSwcLoader;
|
||||
26
src/loaders/main-loaders/ts-loader/index.ts
Normal file
26
src/loaders/main-loaders/ts-loader/index.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import path from 'path';
|
||||
import type webpack from 'webpack';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
const getTsLoader = (options: WebpackOptions): webpack.RuleSetRule | null => {
|
||||
const {
|
||||
loaders: {
|
||||
tsLoader,
|
||||
} = {},
|
||||
} = options;
|
||||
|
||||
if (tsLoader === 'off') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
test: /\.[j|t]sx?$/,
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
...tsLoader,
|
||||
},
|
||||
exclude: path.resolve('node_modules'),
|
||||
};
|
||||
};
|
||||
|
||||
export default getTsLoader;
|
||||
@ -2,9 +2,9 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment, no-unsafe-optional-chaining, @typescript-eslint/no-unsafe-member-access, no-nested-ternary */
|
||||
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
||||
import type webpack from 'webpack';
|
||||
import type { WebpackOptions } from '../../../types';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
export const getCssLoader = (options: WebpackOptions): webpack.RuleSetRule => {
|
||||
const getCssLoader = (options: WebpackOptions): webpack.RuleSetRule => {
|
||||
const {
|
||||
mode,
|
||||
loaders: {
|
||||
@ -39,6 +39,11 @@ export const getCssLoader = (options: WebpackOptions): webpack.RuleSetRule => {
|
||||
options: {
|
||||
sourceMap: isDev,
|
||||
...cssLoader,
|
||||
modules: {
|
||||
localIdentName: '[local]--[hash:5]',
|
||||
// @ts-ignore
|
||||
...cssLoader?.modules,
|
||||
},
|
||||
},
|
||||
},
|
||||
postCssLoader === 'off'
|
||||
@ -46,7 +51,9 @@ export const getCssLoader = (options: WebpackOptions): webpack.RuleSetRule => {
|
||||
: {
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
...postCssLoader,
|
||||
postcssOptions: {
|
||||
...postCssLoader?.postcssOptions,
|
||||
plugins: [
|
||||
[
|
||||
'postcss-preset-env',
|
||||
@ -57,11 +64,12 @@ export const getCssLoader = (options: WebpackOptions): webpack.RuleSetRule => {
|
||||
],
|
||||
...postCssLoader?.postcssOptions?.plugins ?? [],
|
||||
].filter(Boolean),
|
||||
...postCssLoader?.postcssOptions,
|
||||
},
|
||||
...postCssLoader,
|
||||
sourceMap: isDev,
|
||||
},
|
||||
},
|
||||
].filter(Boolean),
|
||||
};
|
||||
};
|
||||
|
||||
export default getCssLoader;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type webpack from 'webpack';
|
||||
import type { WebpackOptions } from '../../types';
|
||||
import { getCssLoader } from './css-loader';
|
||||
import { getSassLoader } from './sass-loader';
|
||||
import type { WebpackOptions } from 'types';
|
||||
import getCssLoader from './css-loader';
|
||||
import getSassLoader from './sass-loader';
|
||||
|
||||
export const getStyleLoaders = (options: WebpackOptions): (webpack.RuleSetRule | null)[] => [
|
||||
getCssLoader(options),
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import type webpack from 'webpack';
|
||||
import type { WebpackOptions } from '../../../types';
|
||||
import { getCssLoader } from '../css-loader';
|
||||
import type { WebpackOptions } from 'types';
|
||||
import getCssLoader from '../css-loader';
|
||||
|
||||
export const getSassLoader = (options: WebpackOptions): webpack.RuleSetRule | null => {
|
||||
const getSassLoader = (options: WebpackOptions): webpack.RuleSetRule | null => {
|
||||
const {
|
||||
mode,
|
||||
loaders: {
|
||||
@ -30,3 +30,5 @@ export const getSassLoader = (options: WebpackOptions): webpack.RuleSetRule | nu
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
export default getSassLoader;
|
||||
|
||||
20
src/loaders/svg-loaders/index.ts
Normal file
20
src/loaders/svg-loaders/index.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import getInlineSvgLoader from 'loaders/svg-loaders/inline-svg-loader';
|
||||
import getSvgrLoader from 'loaders/svg-loaders/svgr-loader';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
const getSvgLoader = (options: WebpackOptions) => {
|
||||
const {
|
||||
svgLoader = 'svgr',
|
||||
} = options;
|
||||
|
||||
switch (svgLoader) {
|
||||
case 'inline':
|
||||
return getInlineSvgLoader(options);
|
||||
|
||||
case 'svgr':
|
||||
default:
|
||||
return getSvgrLoader(options);
|
||||
}
|
||||
};
|
||||
|
||||
export default getSvgLoader;
|
||||
24
src/loaders/svg-loaders/inline-svg-loader/index.ts
Normal file
24
src/loaders/svg-loaders/inline-svg-loader/index.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
const getInlineSvgLoader = (options: WebpackOptions) => {
|
||||
const {
|
||||
loaders: {
|
||||
inlineSvgLoader,
|
||||
} = {},
|
||||
} = options;
|
||||
|
||||
if (inlineSvgLoader === 'off') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
test: /\.svg$/i,
|
||||
type: 'asset/source',
|
||||
generator: {
|
||||
filename: 'static/icons/[hash][ext][query]',
|
||||
...inlineSvgLoader,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default getInlineSvgLoader;
|
||||
@ -1,7 +1,7 @@
|
||||
import type webpack from 'webpack';
|
||||
import type { WebpackOptions } from '../../types';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
export const getSvgLoader = (options: WebpackOptions): webpack.RuleSetRule | null => {
|
||||
const getSvgrLoader = (options: WebpackOptions): webpack.RuleSetRule | null => {
|
||||
const {
|
||||
loaders: {
|
||||
svgrLoader,
|
||||
@ -24,3 +24,5 @@ export const getSvgLoader = (options: WebpackOptions): webpack.RuleSetRule | nul
|
||||
}],
|
||||
};
|
||||
};
|
||||
|
||||
export default getSvgrLoader;
|
||||
@ -1,58 +0,0 @@
|
||||
import type webpack from 'webpack';
|
||||
import type { WebpackOptions } from '../../types';
|
||||
|
||||
export const getSwcLoader = (options: WebpackOptions): webpack.RuleSetRule | null => {
|
||||
const {
|
||||
mode,
|
||||
loaders: {
|
||||
swcLoader,
|
||||
} = {},
|
||||
} = options;
|
||||
const isDev = mode === 'development';
|
||||
|
||||
if (swcLoader === 'off') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
test: /\.[j|t]sx?$/,
|
||||
exclude: /(node_modules|bower_components)/,
|
||||
use: {
|
||||
loader: 'swc-loader',
|
||||
options: {
|
||||
sync: true,
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
tsx: true,
|
||||
dynamicImport: true,
|
||||
privateMethod: true,
|
||||
functionBind: true,
|
||||
exportDefaultFrom: true,
|
||||
exportNamespaceFrom: true,
|
||||
decorators: true,
|
||||
decoratorsBeforeExport: true,
|
||||
topLevelAwait: true,
|
||||
importMeta: true,
|
||||
...swcLoader?.jsc?.parser,
|
||||
},
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'automatic',
|
||||
development: isDev,
|
||||
refresh: isDev,
|
||||
...swcLoader?.jsc?.transform?.react,
|
||||
},
|
||||
...swcLoader?.jsc?.transform,
|
||||
},
|
||||
target: 'es2015',
|
||||
loose: false,
|
||||
externalHelpers: false,
|
||||
keepClassNames: false,
|
||||
...swcLoader?.jsc,
|
||||
},
|
||||
...swcLoader,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
@ -1,9 +1,9 @@
|
||||
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin';
|
||||
import TerserPlugin from 'terser-webpack-plugin';
|
||||
import type webpack from 'webpack';
|
||||
import type { WebpackOptions } from '../types';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
export const getOptimization = (options: WebpackOptions): webpack.Configuration['optimization'] => {
|
||||
const getOptimization = (options: WebpackOptions): webpack.Configuration['optimization'] => {
|
||||
const {
|
||||
mode,
|
||||
} = options;
|
||||
@ -24,3 +24,5 @@ export const getOptimization = (options: WebpackOptions): webpack.Configuration[
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default getOptimization;
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import path from 'path';
|
||||
import type webpack from 'webpack';
|
||||
import type { WebpackOptions } from '../types';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
export const createOutput = (options: WebpackOptions): webpack.Configuration['output'] => {
|
||||
const createOutput = (options: WebpackOptions): webpack.Configuration['output'] => {
|
||||
const {
|
||||
paths,
|
||||
mode,
|
||||
@ -27,3 +27,5 @@ export const createOutput = (options: WebpackOptions): webpack.Configuration['ou
|
||||
|
||||
return isDev ? devOutput : productionOutput;
|
||||
};
|
||||
|
||||
export default createOutput;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
|
||||
import type { WebpackOptions } from '../../types';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
export const getBundleAnalyzerPlugin = (options: WebpackOptions) => {
|
||||
const getBundleAnalyzerPlugin = (options: WebpackOptions) => {
|
||||
const {
|
||||
analyze,
|
||||
plugins: {
|
||||
@ -15,3 +15,5 @@ export const getBundleAnalyzerPlugin = (options: WebpackOptions) => {
|
||||
|
||||
return new BundleAnalyzerPlugin(analyzerPlugin);
|
||||
};
|
||||
|
||||
export default getBundleAnalyzerPlugin;
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import CopyPlugin from 'copy-webpack-plugin';
|
||||
import path from 'path';
|
||||
import type { WebpackOptions } from '../../types';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
export const getCopyPlugin = (options: WebpackOptions) => {
|
||||
const getCopyPlugin = (options: WebpackOptions) => {
|
||||
const {
|
||||
paths,
|
||||
plugins: {
|
||||
@ -25,10 +25,12 @@ export const getCopyPlugin = (options: WebpackOptions) => {
|
||||
],
|
||||
},
|
||||
to: path.resolve(paths.root, paths.build),
|
||||
noErrorOnMissing: true,
|
||||
},
|
||||
|
||||
...copyPlugin?.patterns ?? [],
|
||||
].filter(Boolean),
|
||||
options: copyPlugin?.options,
|
||||
});
|
||||
};
|
||||
|
||||
export default getCopyPlugin;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import webpack from 'webpack';
|
||||
import type { WebpackOptions } from '../../types';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
export const getDefinePlugin = (options: WebpackOptions) => {
|
||||
const getDefinePlugin = (options: WebpackOptions) => {
|
||||
const {
|
||||
mode,
|
||||
plugins: {
|
||||
@ -20,3 +20,5 @@ export const getDefinePlugin = (options: WebpackOptions) => {
|
||||
...definePlugin,
|
||||
});
|
||||
};
|
||||
|
||||
export default getDefinePlugin;
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import ESLintPlugin from 'eslint-webpack-plugin';
|
||||
import path from 'path';
|
||||
import type { WebpackOptions } from '../../types';
|
||||
import type { WebpackOptions } from 'types';
|
||||
import { hasPackage } from 'utils/helpers';
|
||||
|
||||
export const getEslintPlugin = (options: WebpackOptions) => {
|
||||
const getEslintPlugin = (options: WebpackOptions) => {
|
||||
const {
|
||||
paths,
|
||||
plugins: {
|
||||
@ -10,36 +11,21 @@ export const getEslintPlugin = (options: WebpackOptions) => {
|
||||
} = {},
|
||||
} = options;
|
||||
|
||||
const hasEslintPackage = Boolean(process.env.npm_package_dependencies_eslint
|
||||
?? process.env.npm_package_devDependencies_eslint);
|
||||
|
||||
if (!hasEslintPackage || eslintPlugin === 'off') {
|
||||
if (!hasPackage('eslint') || eslintPlugin === 'off') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const getExcludeArray = () => {
|
||||
// todo fixme
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
if (!eslintPlugin?.exclude) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (Array.isArray(eslintPlugin.exclude)) {
|
||||
return eslintPlugin.exclude;
|
||||
}
|
||||
|
||||
return [eslintPlugin.exclude];
|
||||
};
|
||||
|
||||
return new ESLintPlugin({
|
||||
extensions: ['.js', '.jsx', '.tsx', '.ts'],
|
||||
failOnError: false,
|
||||
lintDirtyModulesOnly: true,
|
||||
...eslintPlugin,
|
||||
exclude: [
|
||||
path.resolve(paths.root, 'node_modules'),
|
||||
paths.build,
|
||||
...getExcludeArray(),
|
||||
...eslintPlugin?.exclude ?? [],
|
||||
].filter(Boolean),
|
||||
...eslintPlugin,
|
||||
});
|
||||
};
|
||||
|
||||
export default getEslintPlugin;
|
||||
|
||||
@ -1,16 +1,24 @@
|
||||
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
|
||||
import type { WebpackOptions } from '../../types';
|
||||
import type { WebpackOptions } from 'types';
|
||||
import { hasPackage } from 'utils/helpers';
|
||||
|
||||
export const getForkTsCheckerPlugin = (options: WebpackOptions) => {
|
||||
const getForkTsCheckerPlugin = (options: WebpackOptions) => {
|
||||
const {
|
||||
plugins: {
|
||||
forkTsCheckerPlugin,
|
||||
} = {},
|
||||
} = options;
|
||||
|
||||
if (forkTsCheckerPlugin === 'off') {
|
||||
if (forkTsCheckerPlugin === 'off' || !hasPackage('typescript')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ForkTsCheckerWebpackPlugin(forkTsCheckerPlugin);
|
||||
return new ForkTsCheckerWebpackPlugin({
|
||||
...forkTsCheckerPlugin,
|
||||
typescript: {
|
||||
...forkTsCheckerPlugin?.typescript,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default getForkTsCheckerPlugin;
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import fs from 'fs';
|
||||
import HTMLWebpackPlugin from 'html-webpack-plugin';
|
||||
import path from 'path';
|
||||
import type { WebpackOptions } from '../../types';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
export const getHtmlPlugin = (options: WebpackOptions) => {
|
||||
const getHtmlPlugin = (options: WebpackOptions) => {
|
||||
const {
|
||||
paths,
|
||||
plugins: {
|
||||
@ -14,10 +15,18 @@ export const getHtmlPlugin = (options: WebpackOptions) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!fs.existsSync(paths.html)) {
|
||||
throw new Error(`index.html file not found. Define path to index.html file or create one in "${paths.public}" directory`);
|
||||
}
|
||||
|
||||
const isExistFavicon = fs.existsSync(path.resolve(paths.assets, 'favicon.ico'));
|
||||
|
||||
return new HTMLWebpackPlugin({
|
||||
template: paths.html,
|
||||
favicon: path.resolve(paths.assets, 'favicon.ico'),
|
||||
favicon: isExistFavicon && path.resolve(paths.assets, 'favicon.ico'),
|
||||
title: 'My App',
|
||||
...htmlWebpackPlugin,
|
||||
});
|
||||
};
|
||||
|
||||
export default getHtmlPlugin;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import webpack from 'webpack';
|
||||
import type { WebpackOptions } from '../../types';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
export const getIgnorePlugin = (options: WebpackOptions) => {
|
||||
const getIgnorePlugin = (options: WebpackOptions) => {
|
||||
const {
|
||||
plugins: {
|
||||
ignorePlugin,
|
||||
@ -18,3 +18,5 @@ export const getIgnorePlugin = (options: WebpackOptions) => {
|
||||
...ignorePlugin,
|
||||
});
|
||||
};
|
||||
|
||||
export default getIgnorePlugin;
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import type webpack from 'webpack';
|
||||
import type { WebpackOptions } from '../types';
|
||||
import { getBundleAnalyzerPlugin } from './bundle-analyzer-plugin';
|
||||
import { getCopyPlugin } from './copy-plugin';
|
||||
import { getDefinePlugin } from './define-plugin';
|
||||
import { getEslintPlugin } from './eslint-plugin';
|
||||
import { getForkTsCheckerPlugin } from './fork-ts-checker-plugin';
|
||||
import { getHtmlPlugin } from './html-plugin';
|
||||
import { getIgnorePlugin } from './ignore-plugin';
|
||||
import { getMiniCssPlugin } from './mini-css-plugin';
|
||||
import { getProgressPlugin } from './progress';
|
||||
import { getReactRefreshPlugin } from './react-refresh-plugin';
|
||||
import type { WebpackOptions } from 'types';
|
||||
import getBundleAnalyzerPlugin from './bundle-analyzer-plugin';
|
||||
import getCopyPlugin from './copy-plugin';
|
||||
import getDefinePlugin from './define-plugin';
|
||||
import getEslintPlugin from './eslint-plugin';
|
||||
import getForkTsCheckerPlugin from './fork-ts-checker-plugin';
|
||||
import getHtmlPlugin from './html-plugin';
|
||||
import getIgnorePlugin from './ignore-plugin';
|
||||
import getMiniCssPlugin from './mini-css-plugin';
|
||||
import getProgressPlugin from './progress';
|
||||
import getReactRefreshPlugin from './react-refresh-plugin';
|
||||
|
||||
export const createPlugins = (options: WebpackOptions): webpack.Configuration['plugins'] => {
|
||||
const createPlugins = (options: WebpackOptions): webpack.Configuration['plugins'] => {
|
||||
const {
|
||||
mode,
|
||||
} = options;
|
||||
@ -39,5 +39,8 @@ export const createPlugins = (options: WebpackOptions): webpack.Configuration['p
|
||||
|
||||
const plugins = isDev ? developmentPlugins : productionPlugins;
|
||||
|
||||
// @ts-ignore
|
||||
return plugins.filter(Boolean);
|
||||
};
|
||||
|
||||
export default createPlugins;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
||||
import type { WebpackOptions } from '../../types';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
export const getMiniCssPlugin = (options: WebpackOptions) => {
|
||||
const getMiniCssPlugin = (options: WebpackOptions) => {
|
||||
const {
|
||||
plugins: {
|
||||
miniCssPlugin,
|
||||
@ -18,3 +18,5 @@ export const getMiniCssPlugin = (options: WebpackOptions) => {
|
||||
...miniCssPlugin,
|
||||
});
|
||||
};
|
||||
|
||||
export default getMiniCssPlugin;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import webpack from 'webpack';
|
||||
import type { WebpackOptions } from '../../types';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
export const getProgressPlugin = (options: WebpackOptions) => {
|
||||
const getProgressPlugin = (options: WebpackOptions) => {
|
||||
const {
|
||||
plugins: {
|
||||
progressPlugin,
|
||||
@ -14,3 +14,5 @@ export const getProgressPlugin = (options: WebpackOptions) => {
|
||||
|
||||
return new webpack.ProgressPlugin(progressPlugin);
|
||||
};
|
||||
|
||||
export default getProgressPlugin;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
|
||||
import type { WebpackOptions } from '../../types';
|
||||
import type { WebpackOptions } from 'types';
|
||||
|
||||
export const getReactRefreshPlugin = (options: WebpackOptions) => {
|
||||
const getReactRefreshPlugin = (options: WebpackOptions) => {
|
||||
const {
|
||||
plugins: {
|
||||
reactRefreshPlugin,
|
||||
@ -17,3 +17,5 @@ export const getReactRefreshPlugin = (options: WebpackOptions) => {
|
||||
...reactRefreshPlugin,
|
||||
});
|
||||
};
|
||||
|
||||
export default getReactRefreshPlugin;
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import path from 'path';
|
||||
|
||||
import type webpack from 'webpack';
|
||||
import type { WebpackOptions } from '../types';
|
||||
import type { WebpackOptions } from 'types';
|
||||
import getResolversPlugins from './plugins';
|
||||
|
||||
export const createResolvers = (options: WebpackOptions): webpack.ResolveOptions => {
|
||||
const createResolvers = (options: WebpackOptions): webpack.ResolveOptions => {
|
||||
const {
|
||||
paths,
|
||||
} = options;
|
||||
@ -16,5 +17,8 @@ export const createResolvers = (options: WebpackOptions): webpack.ResolveOptions
|
||||
path.resolve(paths.root),
|
||||
path.resolve(paths.root, 'node_modules'),
|
||||
],
|
||||
plugins: getResolversPlugins(options),
|
||||
};
|
||||
};
|
||||
|
||||
export default createResolvers;
|
||||
|
||||
9
src/resolvers/plugins/index.ts
Normal file
9
src/resolvers/plugins/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import type webpack from 'webpack';
|
||||
import type { WebpackOptions } from 'types';
|
||||
import getTsConfigPathsPlugin from './tsconfig-paths-webpack-plugin';
|
||||
|
||||
const getResolversPlugins = (options: WebpackOptions): webpack.ResolveOptions['plugins'] => [
|
||||
getTsConfigPathsPlugin(options),
|
||||
].filter(Boolean);
|
||||
|
||||
export default getResolversPlugins;
|
||||
25
src/resolvers/plugins/tsconfig-paths-webpack-plugin/index.ts
Normal file
25
src/resolvers/plugins/tsconfig-paths-webpack-plugin/index.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import process from 'process';
|
||||
import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
|
||||
import type { WebpackOptions } from 'types';
|
||||
import { hasPackage } from 'utils/helpers';
|
||||
|
||||
const getTsConfigPathsPlugin = (options: WebpackOptions) => {
|
||||
const {
|
||||
resolvers: {
|
||||
plugins: {
|
||||
tsConfigPathsPlugin = {},
|
||||
} = {},
|
||||
} = {},
|
||||
} = options;
|
||||
|
||||
if (tsConfigPathsPlugin === 'off' || !hasPackage('typescript')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new TsconfigPathsPlugin({
|
||||
baseUrl: process.cwd(),
|
||||
...tsConfigPathsPlugin,
|
||||
});
|
||||
};
|
||||
|
||||
export default getTsConfigPathsPlugin;
|
||||
@ -12,10 +12,17 @@ import type { PostCSSLoaderOptions } from 'postcss-loader/dist/config';
|
||||
import type webpack from 'webpack';
|
||||
import type { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
|
||||
import type SassLoader from 'sass-loader';
|
||||
import type { Options as TsconfigPathsPluginOptions } from 'tsconfig-paths-webpack-plugin/lib/options';
|
||||
import type { Options as TsLoaderOptions } from 'ts-loader';
|
||||
|
||||
type PackageJson = {
|
||||
dependencies: Record<string, string>;
|
||||
devDependencies: Record<string, string>;
|
||||
};
|
||||
|
||||
type WebpackMode = 'development' | 'production';
|
||||
|
||||
type WebpackMainLoaders = 'esbuild' | 'swc';
|
||||
type WebpackMainLoaders = 'babel' | 'esbuild' | 'swc' | 'ts-loader';
|
||||
|
||||
type WebpackPaths = {
|
||||
entry: string[] | string;
|
||||
@ -130,11 +137,89 @@ declare namespace CssLoader {
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
declare namespace BabelLoader {
|
||||
type CallerData = {
|
||||
name: string;
|
||||
supportsStaticESM?: boolean;
|
||||
supportsDynamicImport?: boolean;
|
||||
supportsTopLevelAwait?: boolean;
|
||||
supportsExportNamespaceFrom?: boolean;
|
||||
};
|
||||
|
||||
type MatchPatternFunc = (
|
||||
filename: string,
|
||||
context: { caller?: { name: string }; envName: string; dirname: string }
|
||||
) => boolean;
|
||||
type MatchPattern = MatchPatternFunc | RegExp | string;
|
||||
|
||||
type EntryTarget = () => Record<string, unknown> | string ;
|
||||
type EntryOptions = Record<string, unknown> | false | undefined;
|
||||
type PluginEntry = EntryTarget | [EntryTarget, EntryOptions, string] | [EntryTarget, EntryOptions];
|
||||
type PresetEntry = EntryTarget | [EntryTarget, EntryOptions, string] | [EntryTarget, EntryOptions];
|
||||
type Options = {
|
||||
cwd?: string;
|
||||
caller?: CallerData;
|
||||
filename?: string;
|
||||
filenameRelative?: string;
|
||||
code?: boolean;
|
||||
ast?: boolean;
|
||||
cloneInputAst?: boolean;
|
||||
root?: string;
|
||||
rootMode?: 'root' | 'upward-optional' | 'upward';
|
||||
envName?: string;
|
||||
configFile?: boolean | string;
|
||||
babelrc?: boolean;
|
||||
babelrcRoots?: MatchPattern | MatchPattern[] | boolean;
|
||||
plugins?: PluginEntry[];
|
||||
presets?: {
|
||||
env: EntryOptions;
|
||||
react: EntryOptions;
|
||||
typescript: EntryOptions;
|
||||
};
|
||||
passPerPreset?: boolean;
|
||||
targets?: Record<string, string> | string[] | string;
|
||||
browserslistConfigFile?: boolean;
|
||||
browserslistEnv?: string;
|
||||
extends?: string;
|
||||
env?: Record<string, unknown>;
|
||||
overrides?: unknown[];
|
||||
test?: MatchPattern | MatchPattern[];
|
||||
include?: MatchPattern | MatchPattern[];
|
||||
exclude?: MatchPattern | MatchPattern[];
|
||||
ignore?: MatchPattern[];
|
||||
only?: MatchPattern[];
|
||||
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
|
||||
inputSourceMap?: webpack.Configuration['devtool'] | boolean; // todo
|
||||
sourceMaps?: boolean | 'both' | 'inline';
|
||||
sourceFileName?: string;
|
||||
sourceRoot?: string;
|
||||
sourceType?: 'module' | 'script' | 'unambiguous';
|
||||
assumptions?: Record<string, boolean>;
|
||||
highlightCode?: boolean;
|
||||
wrapPluginVisitorMethod?: unknown;
|
||||
parserOpts?: Record<string, unknown>;
|
||||
generatorOpts?: Record<string, unknown>;
|
||||
retainLines?: boolean;
|
||||
compact?: boolean | 'auto';
|
||||
minified?: boolean;
|
||||
auxiliaryCommentBefore?: string;
|
||||
auxiliaryCommentAfter?: string;
|
||||
comments?: boolean;
|
||||
shouldPrintComment?: (value: string) => boolean;
|
||||
moduleIds?: boolean;
|
||||
moduleId?: string;
|
||||
getModuleId?: (name: string) => string;
|
||||
moduleRoot?: string;
|
||||
};
|
||||
}
|
||||
|
||||
type WebpackOptions = {
|
||||
mode: WebpackMode;
|
||||
paths: WebpackPaths;
|
||||
port: number;
|
||||
mainLoader: WebpackMainLoaders;
|
||||
mainLoader?: WebpackMainLoaders;
|
||||
svgLoader?: 'inline' | 'svgr';
|
||||
analyze: boolean;
|
||||
devtool?: webpack.Configuration['devtool'];
|
||||
plugins?: {
|
||||
@ -153,6 +238,7 @@ type WebpackOptions = {
|
||||
esbuildLoader?: EsbuildLoaderOptions | 'off';
|
||||
swcLoader?: SwcLoaderOptions | 'off';
|
||||
svgrLoader?: SvgrLoaderOptions | 'off';
|
||||
inlineSvgLoader?: AssetsLoader.Options | 'off';
|
||||
styleLoader?: StyleLoader.Options | 'off';
|
||||
cssLoader?: CssLoader.Options | 'off';
|
||||
miniCssLoader?: MiniCssLoaderOptions | 'off';
|
||||
@ -161,7 +247,15 @@ type WebpackOptions = {
|
||||
sassLoader?: SassLoader.Options | 'off';
|
||||
imageLoader?: AssetsLoader.Options | 'off';
|
||||
fontLoader?: AssetsLoader.Options | 'off';
|
||||
babelLoader?: BabelLoader.Options | 'off';
|
||||
tsLoader?: TsLoaderOptions | 'off';
|
||||
};
|
||||
resolvers?: {
|
||||
plugins: {
|
||||
tsConfigPathsPlugin?: TsconfigPathsPluginOptions | 'off';
|
||||
};
|
||||
};
|
||||
cache?: webpack.Configuration['cache'] | 'off';
|
||||
};
|
||||
|
||||
type WebpackEnv = {
|
||||
@ -177,4 +271,5 @@ export type {
|
||||
WebpackOptions,
|
||||
WebpackEnv,
|
||||
WebpackMainLoaders,
|
||||
PackageJson,
|
||||
};
|
||||
|
||||
13
src/utils/helpers.ts
Normal file
13
src/utils/helpers.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import fs from 'fs';
|
||||
import type { PackageJson } from 'types';
|
||||
|
||||
export const hasPackage = (packageName: string) => {
|
||||
if (!packageName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-non-null-assertion
|
||||
const packageJson: PackageJson = JSON.parse(fs.readFileSync(process.env.npm_package_json!, 'utf8'));
|
||||
|
||||
return Boolean(packageJson.dependencies[packageName] || packageJson.devDependencies[packageName]);
|
||||
};
|
||||
@ -12,7 +12,10 @@
|
||||
"target": "ESNext",
|
||||
"lib": [
|
||||
"DOM"
|
||||
]
|
||||
],
|
||||
"paths": {
|
||||
"*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user