feat(TEAMMSBMOB-14836): настройка монорепозитория
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
.npmrc
|
||||
.DS_Store
|
||||
build
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx --no-install -- commitlint --edit "$1"
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
# #!/bin/sh
|
||||
# . "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
# npx lerna run precommit --stream --no-bail --concurrency 1
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('@eco/lint-staged-config/extended');
|
||||
@@ -0,0 +1,2 @@
|
||||
registry=https://nexus-npm.gboteam.ru/
|
||||
//nexus.gboteam.ru/repository/:_auth=""
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: ['@eco/stylelint-config'],
|
||||
};
|
||||
@@ -0,0 +1,124 @@
|
||||
# Монорепозиторий интернет-банка МСБ
|
||||
|
||||
## 🌐 Окружение
|
||||
|
||||
- Node.js: **v20.14.0**
|
||||
- NPM: **10.7.0**
|
||||
|
||||
## 🧑💻 Быстрый старт
|
||||
|
||||
1. Установить зависимости:
|
||||
|
||||
```bash
|
||||
npm ci
|
||||
```
|
||||
|
||||
2. Запустить микрофронты в dev-режиме:
|
||||
|
||||
```bash
|
||||
npm run start
|
||||
```
|
||||
3. Запустить сборку микрофронтов в режиме production:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
4. Запустить сборку микрофронтов в режиме development:
|
||||
|
||||
```bash
|
||||
npm run build:dev
|
||||
```
|
||||
5. Запустить мок сервер по отдаче конфиг файла mfs.json:
|
||||
|
||||
```bash
|
||||
npm run mfs-config
|
||||
```
|
||||
6. Настройка микрофронтового модуля
|
||||
Создать `webpack.config.ts` с параметрами конфигурации:
|
||||
|
||||
```ts
|
||||
/** webpack.config.ts */
|
||||
import type { IWebpackAppConfig } from '@msb/mf-builder';
|
||||
import { normalizePackageName } from '@msb/mf-builder';
|
||||
import packageJson from './package.json';
|
||||
|
||||
const config: WebpackAppConfig = {
|
||||
paths: { entryPath: '/', publicPath: '/', outputPath: '/', srcPath: '/', publicUrl: '/' },
|
||||
devServerOptions: { port: 3001, open: true },
|
||||
moduleFederationOptions: {
|
||||
exposes: {
|
||||
'./App': path.resolve(__dirname, 'src/exposes/App.tsx'),
|
||||
},
|
||||
shared: {
|
||||
'@fractal-ui/styling': {
|
||||
singleton: true,
|
||||
requiredVersion: packageJson.dependencies['@fractal-ui/styling'],
|
||||
},
|
||||
'@fractal-ui/core': {
|
||||
singleton: true,
|
||||
requiredVersion: packageJson.dependencies['@fractal-ui/core'],
|
||||
},
|
||||
'@fractal-ui/extended': {
|
||||
singleton: true,
|
||||
requiredVersion: packageJson.dependencies['@fractal-ui/extended'],
|
||||
},
|
||||
'@fractal-ui/composites': {
|
||||
singleton: true,
|
||||
requiredVersion: packageJson.dependencies['@fractal-ui/composites'],
|
||||
},
|
||||
'@fractal-ui/form': {
|
||||
singleton: true,
|
||||
requiredVersion: packageJson.dependencies['@fractal-ui/form'],
|
||||
},
|
||||
'@fractal-ui/layout': {
|
||||
singleton: true,
|
||||
requiredVersion: packageJson.dependencies['@fractal-ui/layout'],
|
||||
},
|
||||
'@fractal-ui/library': {
|
||||
singleton: true,
|
||||
requiredVersion: packageJson.dependencies['@fractal-ui/library'],
|
||||
},
|
||||
'@fractal-ui/overlays': {
|
||||
singleton: true,
|
||||
requiredVersion: packageJson.dependencies['@fractal-ui/overlays'],
|
||||
},
|
||||
'@fractal-ui/table': {
|
||||
singleton: true,
|
||||
requiredVersion: packageJson.dependencies['@fractal-ui/table'],
|
||||
},
|
||||
'@fractal-ui/visualization': {
|
||||
singleton: true,
|
||||
requiredVersion: packageJson.dependencies['@fractal-ui/visualization'],
|
||||
},
|
||||
'@tanstack/react-query': {
|
||||
singleton: true,
|
||||
requiredVersion: packageJson.dependencies['@tanstack/react-query'],
|
||||
},
|
||||
axios: {
|
||||
singleton: true,
|
||||
requiredVersion: packageJson.dependencies.axios,
|
||||
},
|
||||
react: {
|
||||
singleton: true,
|
||||
eager: true,
|
||||
requiredVersion: packageJson.dependencies.react,
|
||||
},
|
||||
'react-dom': {
|
||||
singleton: true,
|
||||
eager: true,
|
||||
requiredVersion: packageJson.dependencies['react-dom'],
|
||||
},
|
||||
'react-router-dom': {
|
||||
singleton: true,
|
||||
requiredVersion: packageJson.dependencies['react-router-dom'],
|
||||
},
|
||||
'styled-components': {
|
||||
singleton: true,
|
||||
requiredVersion: packageJson.dependencies['styled-components'],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
```
|
||||
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
rules: {
|
||||
'type-enum': [2, 'always', ['story', 'feat', 'fix', 'bugfix', 'release', 'task', 'team']],
|
||||
'scope-empty': [2, 'never'],
|
||||
'scope-case': [0],
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
0 silly argv [object Object]
|
||||
1 notice cli v2.6.0
|
||||
2 verbose rootPath /Users/user/Documents/gpb/msb-platform-monorepo
|
||||
3 info versioning independent
|
||||
4 verbose project workspaces packages packages/* services/*
|
||||
5 notice filter including "@msb-services/host"
|
||||
6 info filter ["@msb-services/host"]
|
||||
7 info Executing command in 1 package: "npm run mfs-config"
|
||||
8 verbose Topological npm run mfs-config
|
||||
9 silly npmRunScriptStreaming ["mfs-config",[],"@msb-services/host"]
|
||||
10 silly getNpmExecOpts /Users/user/Documents/gpb/msb-platform-monorepo/services/msb-host
|
||||
11 error TypeError: Received unexpected exit code value undefined
|
||||
11 error at getExitCode (file:///Users/user/Documents/gpb/msb-platform-monorepo/node_modules/@lerna-lite/core/dist/child-process.js:56:11)
|
||||
11 error at file:///Users/user/Documents/gpb/msb-platform-monorepo/node_modules/@lerna-lite/core/dist/child-process.js:80:28
|
||||
11 error at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "node_modules/@lerna-lite/cli/schemas/lerna-schema.json",
|
||||
"version": "independent",
|
||||
"useWorkspaces": true,
|
||||
"exact": true,
|
||||
"syncWorkspaceLock": true
|
||||
}
|
||||
Generated
+30278
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "msb-platform-monorepo",
|
||||
"private": true,
|
||||
"workspaces": ["packages/*", "services/*"],
|
||||
"scripts": {
|
||||
"start": "lerna run start --stream",
|
||||
"build:packages": "lerna run build --scope=@msb/* --stream",
|
||||
"lint": "lerna run lint --scope=msb-* --stream",
|
||||
"lint-fix": "lerna run lint-fix --scope=msb-* --stream",
|
||||
"mfs-config": "lerna run mfs-config --scope=msb-host --stream",
|
||||
"prepare": "husky || true"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@lerna-lite/list": "2.6.0",
|
||||
"@lerna-lite/publish": "2.6.0",
|
||||
"@lerna-lite/run": "2.6.0",
|
||||
"@commitlint/cli": "^16.3.0",
|
||||
"@commitlint/config-conventional": "^8.3.4",
|
||||
"husky": "^7.0.4"
|
||||
},
|
||||
"peerDependencies": { "react": "17.0.2", "react-dom": "17.0.2" }
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
module.exports = {
|
||||
extends: ['@eco/eslint-config'],
|
||||
rules: {
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
'unicorn/filename-case': 'off',
|
||||
'@typescript-eslint/no-floating-promises': 'off',
|
||||
'@typescript-eslint/no-restricted-imports': 'off',
|
||||
'no-underscore-dangle': 'off',
|
||||
'import/no-named-as-default': 'off',
|
||||
'@eco/no-missing-localization': 'off',
|
||||
'react/destructuring-assignment': 'off',
|
||||
'func-style': 'off',
|
||||
'import/no-cycle': 'off',
|
||||
},
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
project: './tsconfig.json',
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "@msb/mf-utils",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "src/index.ts",
|
||||
"devDependencies": {
|
||||
"typescript": "4.5.5",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"@types/react": "17.0.36",
|
||||
"@types/react-dom": "^17.0.2",
|
||||
"@msb/mf-builder": "1.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { createExposedModuleEndpoint } from './createExposedModuleEndpoint';
|
||||
import type { ModuleFederationRuntime, IModuleFactory, IExposedModule } from './types';
|
||||
|
||||
export interface IRemoteContainer {
|
||||
name: string;
|
||||
init(): Promise<ModuleFederationRuntime.Container>;
|
||||
}
|
||||
|
||||
export interface IExposedModuleOptions {
|
||||
// Путь до модуля относительно контейнера или его название.
|
||||
name: string;
|
||||
}
|
||||
|
||||
export class ExposedModule<P, T extends IExposedModule<P>> {
|
||||
private readonly _container: IRemoteContainer;
|
||||
private readonly _options: IExposedModuleOptions;
|
||||
|
||||
public get name(): string {
|
||||
return `${this._container.name}__${this._options.name}`;
|
||||
}
|
||||
|
||||
constructor(container: IRemoteContainer, options: IExposedModuleOptions) {
|
||||
this._container = container;
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузить код модуля.
|
||||
*/
|
||||
load = (): Promise<IModuleFactory<P, T>> => {
|
||||
const { name } = this._options;
|
||||
|
||||
return this._container.init().then(container => container.get(createExposedModuleEndpoint(name)));
|
||||
};
|
||||
|
||||
/**
|
||||
* Инициализировать модуль. При вызове этого метода начнётся выполнение кода модуля.
|
||||
*/
|
||||
init = (): Promise<T> => this.load().then(factory => factory());
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { loadModule } from '../utils';
|
||||
|
||||
import type { IExposedModuleOptions, IRemoteContainer } from './ExposedModule';
|
||||
import { ExposedModule } from './ExposedModule';
|
||||
import { initShareScope } from './initShareScope';
|
||||
import type { ModuleFederationRuntime, IExposedModule } from './types';
|
||||
|
||||
const normalizePackageName = (packageName: string): string => packageName.replace(/-/g, '_').toLowerCase();
|
||||
|
||||
export interface IRemoteContainerOptions {
|
||||
// Адрес `remoteEntry.js` микрофронта
|
||||
url: string;
|
||||
// Название микрофронта remote модуля
|
||||
name: string;
|
||||
}
|
||||
|
||||
export class RemoteContainer implements IRemoteContainer {
|
||||
private readonly _options: IRemoteContainerOptions;
|
||||
|
||||
constructor(options: IRemoteContainerOptions) {
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this._options.name;
|
||||
}
|
||||
|
||||
/** Проверить совместимость модуля с Module Federation. */
|
||||
static isRemoteContainer(loadedModule: unknown): loadedModule is ModuleFederationRuntime.Container {
|
||||
return typeof loadedModule === 'object' && loadedModule !== null && 'get' in loadedModule && 'init' in loadedModule;
|
||||
}
|
||||
|
||||
/** Загрузить код контейнера. */
|
||||
load = (): Promise<unknown> => {
|
||||
const { name, url } = this._options;
|
||||
|
||||
return loadModule({
|
||||
url,
|
||||
moduleName: normalizePackageName(name),
|
||||
});
|
||||
};
|
||||
|
||||
/** Инициализировать контейнер микрофронта. */
|
||||
init = async (): Promise<ModuleFederationRuntime.Container> => {
|
||||
const { name, url } = this._options;
|
||||
|
||||
const [remoteModule, shareScope] = await Promise.all([this.load(), initShareScope()]);
|
||||
|
||||
if (!RemoteContainer.isRemoteContainer(remoteModule)) {
|
||||
throw new Error(`Module ${name}@${url} is not Module Federation container`);
|
||||
}
|
||||
|
||||
await remoteModule.init(shareScope);
|
||||
|
||||
return remoteModule;
|
||||
};
|
||||
|
||||
/** Загрузить модуль из контейнера. */
|
||||
get = <P, M extends IExposedModule<P>>(options: IExposedModuleOptions): ExposedModule<P, M> => new ExposedModule(this, options);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
const MODULE_PATH_REGEXP = /^(\.\/)?(.+?)(\.(j|t)sx?)?$/i;
|
||||
|
||||
export function createExposedModuleEndpoint(modulePath: string): string {
|
||||
const match = modulePath.match(MODULE_PATH_REGEXP);
|
||||
|
||||
if (!match) {
|
||||
throw new Error(`${modulePath} is not valid exposed module key`);
|
||||
}
|
||||
|
||||
const componentName = match[2];
|
||||
|
||||
return `./${componentName}`;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './types';
|
||||
|
||||
export * from './RemoteContainer';
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { ModuleFederationRuntime } from './types';
|
||||
|
||||
export async function initShareScope(): Promise<ModuleFederationRuntime.ShareScope> {
|
||||
if (!__webpack_share_scopes__.default) {
|
||||
await __webpack_init_sharing__('default');
|
||||
}
|
||||
|
||||
return __webpack_share_scopes__.default;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
const SCOPE_REGEX = /^@.*\//;
|
||||
|
||||
export function removeNameScope(packageName: string): string {
|
||||
return packageName.replace(SCOPE_REGEX, '');
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/// <reference types="webpack/module" />
|
||||
|
||||
import type { FC, ReactElement } from 'react';
|
||||
|
||||
export namespace ModuleFederationRuntime {
|
||||
export type ShareScope = typeof __webpack_share_scopes__[string];
|
||||
|
||||
export interface Container {
|
||||
name: string;
|
||||
// Загрузить загрузить входную точку микрофронта с названием `endpoint`.
|
||||
get<P, M extends IExposedModule<P>>(endpoint: string): Promise<IModuleFactory<P, M>>;
|
||||
// Зарегистрировать контейнер в Share Scope.
|
||||
init(shareScope: ShareScope): Promise<void>;
|
||||
}
|
||||
}
|
||||
|
||||
export type IModuleFactory<P, M extends IExposedModule<P>> = () => M;
|
||||
|
||||
export interface ILazyLoadable<P = any> {
|
||||
default: FC<P>;
|
||||
}
|
||||
|
||||
/**
|
||||
* ES-модуль, обрабатываемый `RemoteModule`.
|
||||
*
|
||||
*/
|
||||
export interface IExposedModule<P> {
|
||||
/**
|
||||
* Компонент микрофронта, который может быть встроен в другой микрофронт с помощью Module Federation.
|
||||
*
|
||||
* @param props - Пропсы, переданные хостом.
|
||||
*/
|
||||
default(props: P): ReactElement | null;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './react';
|
||||
|
||||
export * from './core';
|
||||
@@ -0,0 +1,52 @@
|
||||
import type { ReactElement, ReactNode } from 'react';
|
||||
import { Component } from 'react';
|
||||
|
||||
export interface IErrorFallbackProps {
|
||||
// Ошибка, возникшая в одном из дочерних компонентов
|
||||
error: unknown;
|
||||
// Перезагрузить компонент.
|
||||
reload(): void;
|
||||
}
|
||||
|
||||
export interface IErrorBoundaryProps {
|
||||
// Функция для рендера ошибки, произошедшей в процессе рендера дочернего компонента.
|
||||
errorFallback(props: IErrorFallbackProps): ReactElement;
|
||||
// Событие перезагрузки компонента.
|
||||
onReload?(): void;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export type IErrorBoundaryState =
|
||||
| {
|
||||
status: 'error';
|
||||
error: unknown;
|
||||
}
|
||||
| { status: 'ok' };
|
||||
|
||||
export class ErrorBoundary extends Component<IErrorBoundaryProps, IErrorBoundaryState> {
|
||||
static getDerivedStateFromError(error: unknown): IErrorBoundaryState {
|
||||
return { status: 'error', error };
|
||||
}
|
||||
|
||||
state: IErrorBoundaryState = {
|
||||
status: 'ok',
|
||||
};
|
||||
|
||||
reload(): void {
|
||||
this.setState({ status: 'ok' });
|
||||
this.props.onReload?.();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.status === 'error') {
|
||||
const { errorFallback } = this.props;
|
||||
|
||||
return errorFallback({
|
||||
error: this.state.error,
|
||||
reload: () => this.reload(),
|
||||
});
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { ReactElement } from 'react';
|
||||
import { Suspense } from 'react';
|
||||
|
||||
import type { IErrorBoundaryProps } from './ErrorBoundary';
|
||||
import { ErrorBoundary } from './ErrorBoundary';
|
||||
|
||||
export interface IReloadableProps {
|
||||
// Перезагрузить компонент.
|
||||
reload: (() => void) | undefined;
|
||||
}
|
||||
|
||||
export interface ISafeSuspenseProps extends IErrorBoundaryProps {
|
||||
// Функция для рендера индикатора загрузки remote модуля.
|
||||
loader(props: IReloadableProps): ReactElement;
|
||||
}
|
||||
|
||||
export const SafeSuspense = ({ children, errorFallback, loader, onReload }: ISafeSuspenseProps): ReactElement => (
|
||||
<ErrorBoundary errorFallback={errorFallback} onReload={onReload}>
|
||||
<Suspense fallback={loader({ reload: onReload })}>{children}</Suspense>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
@@ -0,0 +1,48 @@
|
||||
import type { FC, ReactElement } from 'react';
|
||||
|
||||
import type { IExposedModule, ILazyLoadable } from '../core';
|
||||
import type { ExposedModule } from '../core/ExposedModule';
|
||||
import type { IErrorFallbackProps } from './ErrorBoundary';
|
||||
import { SafeSuspense } from './SafeSuspense';
|
||||
import { useLazy } from './useLazy';
|
||||
|
||||
export interface ICreateRemoteComponentProps<P, T extends IExposedModule<P>> {
|
||||
// Модуль микрофронта, содержащий компонент.
|
||||
exposedModule: ExposedModule<P, T>;
|
||||
// Функция для рендера индикатора загрузки компонента.
|
||||
renderLoader(): ReactElement;
|
||||
// Функция для рендера ошибки, произошедшей в процессе загрузки компонента.
|
||||
renderError(props: IErrorFallbackProps): ReactElement;
|
||||
}
|
||||
|
||||
// Фабрика компонента микрофронта
|
||||
export function createRemoteComponent<P = Record<string, unknown>>({
|
||||
exposedModule,
|
||||
renderError,
|
||||
renderLoader,
|
||||
}: ICreateRemoteComponentProps<P, IExposedModule<P>>): FC<P> {
|
||||
const prepareComponentModule = async (): Promise<ILazyLoadable<P>> => {
|
||||
const module = await exposedModule.init();
|
||||
const Component = module.default;
|
||||
|
||||
if (typeof Component !== 'function') {
|
||||
throw new Error(`"Remote Component from "${exposedModule.name}" is not a function`);
|
||||
}
|
||||
|
||||
return { default: props => <Component {...props} /> };
|
||||
};
|
||||
|
||||
const RemoteComponent: FC<P> = props => {
|
||||
const [LazyComponent, reload] = useLazy<P>(() => prepareComponentModule());
|
||||
|
||||
return (
|
||||
<SafeSuspense errorFallback={renderError} loader={renderLoader} onReload={reload}>
|
||||
<LazyComponent {...props} />
|
||||
</SafeSuspense>
|
||||
);
|
||||
};
|
||||
|
||||
RemoteComponent.displayName = exposedModule.name;
|
||||
|
||||
return RemoteComponent;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export * from './ErrorBoundary';
|
||||
|
||||
export * from './SafeSuspense';
|
||||
|
||||
export * from './createRemoteComponent';
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { FC } from 'react';
|
||||
import { lazy, useState } from 'react';
|
||||
import type { ILazyLoadable } from '../core';
|
||||
|
||||
export function useLazy<P>(factory: () => Promise<ILazyLoadable<P>>): [Component: FC<P>, reload: () => void] {
|
||||
const [lazyComponent, setLazyComponent] = useState(() => lazy(factory));
|
||||
|
||||
return [lazyComponent as FC<P>, () => setLazyComponent(lazy(factory))];
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './loadModule';
|
||||
export * from './loadScript';
|
||||
@@ -0,0 +1,34 @@
|
||||
import { loadScript } from './loadScript';
|
||||
|
||||
function getGlobalModule<M>(moduleName: string): M | undefined {
|
||||
return (window as Record<string, any>)[moduleName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Параметры загрузки модуля.
|
||||
*/
|
||||
export interface ILoadModuleArg {
|
||||
moduleName: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузить модуль по его url.
|
||||
*/
|
||||
export async function loadModule<M>(arg: ILoadModuleArg): Promise<M> {
|
||||
const { moduleName, url } = arg;
|
||||
|
||||
let moduleInstance = getGlobalModule<M>(moduleName);
|
||||
|
||||
if (!moduleInstance) {
|
||||
await loadScript(url);
|
||||
|
||||
moduleInstance = getGlobalModule<M>(moduleName);
|
||||
}
|
||||
|
||||
if (!moduleInstance) {
|
||||
throw new Error(`Error loading module "${moduleName}" from "${url}"`);
|
||||
}
|
||||
|
||||
return moduleInstance;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// Поиск элемента script по урлу
|
||||
function findScriptElement(url: string): HTMLScriptElement | null {
|
||||
return document.querySelector(`script[src="${url}"]`);
|
||||
}
|
||||
|
||||
// Создание элемента script
|
||||
function createScriptElement(url: string): HTMLScriptElement {
|
||||
const element = document.createElement('script');
|
||||
|
||||
element.type = 'text/javascript';
|
||||
element.async = true;
|
||||
element.src = url;
|
||||
|
||||
document.body.appendChild(element);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
export const loadScript: (url: string) => Promise<void> = url =>
|
||||
new Promise((resolve, reject) => {
|
||||
let element = findScriptElement(url);
|
||||
|
||||
if (!element) {
|
||||
element = createScriptElement(url);
|
||||
}
|
||||
|
||||
const cleanup = () => {
|
||||
element?.remove();
|
||||
};
|
||||
|
||||
element.onload = () => {
|
||||
cleanup();
|
||||
resolve();
|
||||
};
|
||||
|
||||
element.onerror = () => {
|
||||
cleanup();
|
||||
reject(new Error(`Failed to load script ${url}`));
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"lib": ["DOM", "ESNext", "WebWorker"],
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"resolveJsonModule": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"baseUrl": ".",
|
||||
"noEmit": true,
|
||||
"composite": true,
|
||||
"types": ["node"],
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src", ".eslintrc.js"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
lib
|
||||
types
|
||||
@@ -0,0 +1,21 @@
|
||||
module.exports = {
|
||||
extends: ['@eco/eslint-config'],
|
||||
rules: {
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
'unicorn/filename-case': 'off',
|
||||
'@typescript-eslint/no-floating-promises': 'off',
|
||||
'@typescript-eslint/no-restricted-imports': 'off',
|
||||
'no-underscore-dangle': 'off',
|
||||
'import/no-named-as-default': 'off',
|
||||
'@eco/no-missing-localization': 'off',
|
||||
'func-style': 'off',
|
||||
'import/no-cycle': 'off',
|
||||
},
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
project: './tsconfig.json',
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
# @mcb/mf-builder
|
||||
|
||||
Переиспользуемая конфигурация для сборки веб-приложения с помощью **Webpack 5**
|
||||
|
||||
### Установка
|
||||
|
||||
```bash
|
||||
npm install --save-dev @msb/mf-builder
|
||||
```
|
||||
|
||||
### Использование
|
||||
|
||||
Добавить в `package.json` скрипты для запуска и сборки проекта:
|
||||
|
||||
```json
|
||||
/** package.json */
|
||||
{
|
||||
// ...
|
||||
"scripts" {
|
||||
"start": "build-app start",
|
||||
"build": "build-app build",
|
||||
"build:dev": "build-app build:dev"
|
||||
}
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
## Конфигурация
|
||||
|
||||
Создать `webpack.config.ts` с параметрами конфигурации:
|
||||
|
||||
```ts
|
||||
/** webpack.config.ts */
|
||||
import { WebpackAppConfig } from '@msb/mf-builder';
|
||||
|
||||
const config: WebpackAppConfig = {
|
||||
paths: { entryPath: '/', publicPath: '/', outputPath: '/', srcPath: '/', publicUrl: '/' },
|
||||
devServerOptions: { port: 3001, open: true },
|
||||
moduleFederationOptions: {
|
||||
shared: {
|
||||
'@gpb/ui-kit': {
|
||||
singleton: true,
|
||||
},
|
||||
'@gpb/ui-kit-icons': {
|
||||
singleton: true,
|
||||
},
|
||||
'@tanstack/react-query': {
|
||||
singleton: true,
|
||||
},
|
||||
react: {
|
||||
singleton: true,
|
||||
eager: true,
|
||||
},
|
||||
'react-dom': {
|
||||
singleton: true,
|
||||
eager: true,
|
||||
},
|
||||
'styled-components': {
|
||||
singleton: true,
|
||||
eager: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
```
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
require('../lib/cli/index.js');
|
||||
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const config_1 = require("../../config");
|
||||
const utils_1 = require("../../utils");
|
||||
const webpack_1 = __importDefault(require("webpack"));
|
||||
function build({ configPath, mode }) {
|
||||
var _a;
|
||||
const appConfig = ((_a = (0, utils_1.readConfigFile)(configPath)) !== null && _a !== void 0 ? _a : {});
|
||||
const config = (0, config_1.createWebpackConfig)(appConfig, mode);
|
||||
const compiler = (0, webpack_1.default)(config);
|
||||
return new Promise((resolve, reject) => {
|
||||
compiler.run((error, stats) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
}
|
||||
if (stats) {
|
||||
console.log(stats.toString());
|
||||
}
|
||||
resolve(stats);
|
||||
});
|
||||
});
|
||||
}
|
||||
exports.default = build;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.build = exports.start = void 0;
|
||||
var startDevServer_1 = require("./startDevServer");
|
||||
Object.defineProperty(exports, "start", { enumerable: true, get: function () { return __importDefault(startDevServer_1).default; } });
|
||||
var buildMode_1 = require("./buildMode");
|
||||
Object.defineProperty(exports, "build", { enumerable: true, get: function () { return __importDefault(buildMode_1).default; } });
|
||||
@@ -0,0 +1,37 @@
|
||||
"use strict";
|
||||
var __rest = (this && this.__rest) || function (s, e) {
|
||||
var t = {};
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
||||
t[p] = s[p];
|
||||
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
||||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
||||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
||||
t[p[i]] = s[p[i]];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
const config_1 = require("../../config");
|
||||
const utils_1 = require("../../utils");
|
||||
const webpack_1 = __importDefault(require("webpack"));
|
||||
const webpack_dev_server_1 = __importDefault(require("webpack-dev-server"));
|
||||
function start({ configPath }) {
|
||||
var _a;
|
||||
const appConfig = ((_a = (0, utils_1.readConfigFile)(configPath)) !== null && _a !== void 0 ? _a : {});
|
||||
const _b = (0, config_1.createWebpackConfig)(appConfig), { devServer } = _b, config = __rest(_b, ["devServer"]);
|
||||
const compiler = (0, webpack_1.default)(config);
|
||||
const developmentServer = new webpack_dev_server_1.default(devServer, compiler);
|
||||
developmentServer.startCallback(() => {
|
||||
process.stdin.on('end', () => {
|
||||
// @ts-ignore
|
||||
developmentServer.close();
|
||||
process.exit();
|
||||
});
|
||||
});
|
||||
}
|
||||
exports.default = start;
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
/* eslint-disable sonarjs/no-identical-functions */
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const yargs_1 = __importDefault(require("yargs"));
|
||||
const node_path_1 = __importDefault(require("node:path"));
|
||||
const commands_1 = require("./commands");
|
||||
const DEFAULT_CONFIG_FILE_PATH = node_path_1.default.resolve('webpack.config.ts');
|
||||
yargs_1.default
|
||||
.scriptName('build-app')
|
||||
.command(['build'], 'Собрать приложение в production режиме', command => {
|
||||
command.option('config', {
|
||||
alias: 'c',
|
||||
default: DEFAULT_CONFIG_FILE_PATH,
|
||||
type: 'string',
|
||||
description: 'Путь до конфигурационного файла webpack.config.ts',
|
||||
});
|
||||
}, argv => {
|
||||
(0, commands_1.build)({ configPath: argv.config, mode: 'production' });
|
||||
})
|
||||
.command(['build:dev'], 'Собрать приложение в development режиме', command => {
|
||||
command.option('config', {
|
||||
alias: 'c',
|
||||
default: DEFAULT_CONFIG_FILE_PATH,
|
||||
type: 'string',
|
||||
description: 'Путь до конфигурационного файла webpack.config.ts',
|
||||
});
|
||||
}, argv => {
|
||||
(0, commands_1.build)({ configPath: argv.config, mode: 'development' });
|
||||
})
|
||||
.command(['start'], 'Запустить webpack-dev-server', command => {
|
||||
command.option('config', {
|
||||
alias: 'c',
|
||||
default: DEFAULT_CONFIG_FILE_PATH,
|
||||
type: 'string',
|
||||
description: 'Путь до конфигурационного файла webpack.config.ts',
|
||||
});
|
||||
}, argv => {
|
||||
(0, commands_1.start)({ configPath: argv.config });
|
||||
})
|
||||
.help().argv;
|
||||
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.removeDataTestIdBabelPlugin = void 0;
|
||||
var removeDataTestidPlugin_1 = require("./removeDataTestidPlugin");
|
||||
Object.defineProperty(exports, "removeDataTestIdBabelPlugin", { enumerable: true, get: function () { return removeDataTestidPlugin_1.removeDataTestIdBabelPlugin; } });
|
||||
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.removeDataTestIdBabelPlugin = void 0;
|
||||
/**
|
||||
* Плагин для удаления указанных атрибутов из верстки.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
const removeDataTestIdBabelPlugin = () => ({
|
||||
visitor: {
|
||||
Program(path, state) {
|
||||
const forbiddenProps = state.opts.props || [];
|
||||
path.traverse({
|
||||
JSXIdentifier(current) {
|
||||
const nodeName = current.node.name;
|
||||
if (forbiddenProps.includes(nodeName)) {
|
||||
current.parentPath.remove();
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
exports.removeDataTestIdBabelPlugin = removeDataTestIdBabelPlugin;
|
||||
@@ -0,0 +1,17 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
__exportStar(require("./webpack"), exports);
|
||||
@@ -0,0 +1,110 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.WebpackConfigBuilder = void 0;
|
||||
const webpack_merge_1 = require("webpack-merge");
|
||||
const node_path_1 = __importDefault(require("node:path"));
|
||||
const setDevServer_1 = require("./setDevServer");
|
||||
const setModuleOptions_1 = require("./setModuleOptions");
|
||||
const setOptimizationRules_1 = require("./setOptimizationRules");
|
||||
const setPlugins_1 = require("./setPlugins");
|
||||
class WebpackConfigBuilder {
|
||||
constructor(appConfig, mode) {
|
||||
var _a, _b;
|
||||
this.config = {};
|
||||
this.appConfig = appConfig;
|
||||
this.isDevelopmentMode = mode === 'development';
|
||||
this.isProductionMode = mode === 'production';
|
||||
this.cwd = process.cwd();
|
||||
this.srcPath = node_path_1.default.resolve(this.cwd, ((_a = appConfig.paths) === null || _a === void 0 ? void 0 : _a.srcPath) || 'src');
|
||||
const entry = ((_b = appConfig.paths) === null || _b === void 0 ? void 0 : _b.entry) || node_path_1.default.resolve(this.srcPath, 'index.ts');
|
||||
const target = this.isDevelopmentMode ? 'web' : 'browserslist';
|
||||
const devtool = this.isDevelopmentMode ? 'eval-cheap-module-source-map' : 'source-map';
|
||||
this.config = {
|
||||
mode,
|
||||
target,
|
||||
devtool,
|
||||
entry,
|
||||
};
|
||||
}
|
||||
get configuration() {
|
||||
return this.config;
|
||||
}
|
||||
applyOutput() {
|
||||
var _a, _b, _c;
|
||||
const port = (_a = this.appConfig.devServerOptions) === null || _a === void 0 ? void 0 : _a.port;
|
||||
this.config.output = {
|
||||
path: ((_b = this.appConfig.paths) === null || _b === void 0 ? void 0 : _b.outputPath) || node_path_1.default.resolve(this.cwd, 'dist'),
|
||||
filename: '[name].[contenthash:8].js',
|
||||
chunkFilename: '[name].[contenthash:8].js',
|
||||
publicPath: this.isDevelopmentMode && port ? `http://localhost:${port}/` : ((_c = this.appConfig.paths) === null || _c === void 0 ? void 0 : _c.publicUrl) || '/',
|
||||
clean: true,
|
||||
};
|
||||
}
|
||||
applyResolve() {
|
||||
this.config.resolve = {
|
||||
alias: {
|
||||
// FSD alias
|
||||
'@/app': node_path_1.default.resolve(this.srcPath, 'app'),
|
||||
'@/shared': node_path_1.default.resolve(this.srcPath, 'shared'),
|
||||
'@/pages': node_path_1.default.resolve(this.srcPath, 'pages'),
|
||||
'@/widgets': node_path_1.default.resolve(this.srcPath, 'widgets'),
|
||||
'@/features': node_path_1.default.resolve(this.srcPath, 'features'),
|
||||
'@/entities': node_path_1.default.resolve(this.srcPath, 'entities'),
|
||||
// jsx-runtime
|
||||
'react/jsx-dev-runtime': 'react/jsx-dev-runtime.js',
|
||||
'react/jsx-runtime': 'react/jsx-runtime.js',
|
||||
},
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
||||
};
|
||||
}
|
||||
applyDevServer() {
|
||||
var _a;
|
||||
if (this.isDevelopmentMode) {
|
||||
const devServerOptions = Object.assign(Object.assign({}, this.appConfig.devServerOptions), { port: ((_a = this.appConfig.devServerOptions) === null || _a === void 0 ? void 0 : _a.port) || 3001 });
|
||||
this.config.devServer = (0, setDevServer_1.setDevServer)(devServerOptions);
|
||||
}
|
||||
}
|
||||
applyPerfomance() {
|
||||
if (this.isProductionMode) {
|
||||
this.config.performance = {
|
||||
hints: false,
|
||||
maxEntrypointSize: 512000,
|
||||
maxAssetSize: 512000,
|
||||
};
|
||||
}
|
||||
}
|
||||
applyOptimizationRules() {
|
||||
if (this.isProductionMode) {
|
||||
this.config.optimization = (0, setOptimizationRules_1.setOptimizationRules)(this.appConfig.optimizationOptions);
|
||||
}
|
||||
}
|
||||
applyModuleOptions() {
|
||||
this.config.module = (0, setModuleOptions_1.setModuleOptions)({
|
||||
isDevelopmentMode: this.isDevelopmentMode,
|
||||
isProductionMode: this.isProductionMode,
|
||||
});
|
||||
}
|
||||
applyPlugins() {
|
||||
var _a;
|
||||
const publicPath = ((_a = this.appConfig.paths) === null || _a === void 0 ? void 0 : _a.publicPath) || 'public';
|
||||
this.config.plugins =
|
||||
(0, setPlugins_1.setPlugins)({
|
||||
isDevelopmentMode: this.isDevelopmentMode,
|
||||
isProductionMode: this.isProductionMode,
|
||||
publicPath,
|
||||
moduleName: this.appConfig.moduleName,
|
||||
moduleFederationOptions: this.appConfig.moduleFederationOptions,
|
||||
}) || [];
|
||||
const otherPlugins = this.appConfig.plugins;
|
||||
if (Array.isArray(otherPlugins) && Array.isArray(this.config.plugins)) {
|
||||
this.config.plugins.concat(otherPlugins);
|
||||
}
|
||||
}
|
||||
merge(newConfig) {
|
||||
(0, webpack_merge_1.merge)(this.config, newConfig);
|
||||
}
|
||||
}
|
||||
exports.WebpackConfigBuilder = WebpackConfigBuilder;
|
||||
@@ -0,0 +1,20 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createWebpackConfig = void 0;
|
||||
const WebpackConfigBuilder_1 = require("./WebpackConfigBuilder");
|
||||
/**
|
||||
* Создание конфига для сборки.
|
||||
*/
|
||||
const createWebpackConfig = (environmentConfig, mode = 'development') => {
|
||||
const builder = new WebpackConfigBuilder_1.WebpackConfigBuilder(environmentConfig, mode);
|
||||
builder.applyOutput();
|
||||
builder.applyDevServer();
|
||||
builder.applyModuleOptions();
|
||||
builder.applyOptimizationRules();
|
||||
builder.applyPerfomance();
|
||||
builder.applyResolve();
|
||||
builder.applyPlugins();
|
||||
return builder.configuration;
|
||||
};
|
||||
exports.createWebpackConfig = createWebpackConfig;
|
||||
exports.default = exports.createWebpackConfig;
|
||||
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createWebpackConfig = void 0;
|
||||
var createWebpackConfig_1 = require("./createWebpackConfig");
|
||||
Object.defineProperty(exports, "createWebpackConfig", { enumerable: true, get: function () { return createWebpackConfig_1.createWebpackConfig; } });
|
||||
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.setDevServer = void 0;
|
||||
/**
|
||||
* Настройка Webpack Dev Server.
|
||||
*/
|
||||
const setDevServer = (devServerOptions) => (Object.assign({ open: false, hot: true, historyApiFallback: true, client: {
|
||||
overlay: false,
|
||||
} }, devServerOptions));
|
||||
exports.setDevServer = setDevServer;
|
||||
@@ -0,0 +1,23 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.setModuleFederationPlugin = void 0;
|
||||
const utils_1 = require("../../utils");
|
||||
const webpack_1 = require("webpack");
|
||||
/**
|
||||
* Настройка плагина ModuleFederation.
|
||||
*/
|
||||
const setModuleFederationPlugin = ({ moduleName, moduleFederationOptions, }) => new webpack_1.container.ModuleFederationPlugin(Object.assign({ name: (0, utils_1.normalizePackageName)(moduleName), filename: 'remoteEntry.js', shared: {
|
||||
react: {
|
||||
singleton: true,
|
||||
eager: true,
|
||||
},
|
||||
'react-dom': {
|
||||
singleton: true,
|
||||
eager: true,
|
||||
},
|
||||
'styled-components': {
|
||||
singleton: true,
|
||||
eager: true,
|
||||
},
|
||||
} }, moduleFederationOptions));
|
||||
exports.setModuleFederationPlugin = setModuleFederationPlugin;
|
||||
@@ -0,0 +1,49 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.setModuleOptions = void 0;
|
||||
const utils_1 = require("../../utils");
|
||||
const babel_1 = require("../babel");
|
||||
/**
|
||||
* Настройка обработки разных типов модулей.
|
||||
*/
|
||||
const setModuleOptions = ({ isDevelopmentMode, isProductionMode }) => {
|
||||
const babelPlugins = ['babel-plugin-styled-components'];
|
||||
if (isDevelopmentMode) {
|
||||
babelPlugins.push(require.resolve('react-refresh/babel'));
|
||||
}
|
||||
if (isProductionMode) {
|
||||
babelPlugins.push([
|
||||
babel_1.removeDataTestIdBabelPlugin,
|
||||
{
|
||||
props: ['data-testid'],
|
||||
},
|
||||
]);
|
||||
}
|
||||
return {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(png|svg|jpg|jpeg|gif|woff|woff2|eot|ttf|otf)$/i,
|
||||
type: 'asset/resource',
|
||||
generator: {
|
||||
filename: (0, utils_1.createFileName)({ directoryType: 'assets', isProductionMode }),
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.(css)$/,
|
||||
use: ['style-loader', 'css-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.(ts|js)x?$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: ['@babel/preset-env', ['@babel/preset-react', { runtime: 'automatic' }], '@babel/preset-typescript'],
|
||||
plugins: babelPlugins,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
exports.setModuleOptions = setModuleOptions;
|
||||
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.setOptimizationRules = void 0;
|
||||
const terser_webpack_plugin_1 = __importDefault(require("terser-webpack-plugin"));
|
||||
/**
|
||||
* Установка правил оптимизации сборки.
|
||||
*/
|
||||
const setOptimizationRules = (options) => (Object.assign({ minimize: true, minimizer: [new terser_webpack_plugin_1.default()], splitChunks: {
|
||||
chunks: 'all',
|
||||
maxInitialRequests: Number.POSITIVE_INFINITY,
|
||||
minSize: 0,
|
||||
cacheGroups: {
|
||||
vendor: {
|
||||
test: /[/\\]node_modules[/\\]/,
|
||||
name(module) {
|
||||
var _a;
|
||||
const packageName = (_a = module.context.match(/[/\\]node_modules[/\\](.*?)([/\\]|$)/)) !== null && _a !== void 0 ? _a : 'package';
|
||||
return `npm.${packageName[1].replace('@', '')}`;
|
||||
},
|
||||
},
|
||||
},
|
||||
} }, options));
|
||||
exports.setOptimizationRules = setOptimizationRules;
|
||||
@@ -0,0 +1,48 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.setPlugins = void 0;
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
const react_refresh_webpack_plugin_1 = __importDefault(require("@pmmmwh/react-refresh-webpack-plugin"));
|
||||
const dotenv_webpack_1 = __importDefault(require("dotenv-webpack"));
|
||||
const fork_ts_checker_webpack_plugin_1 = __importDefault(require("fork-ts-checker-webpack-plugin"));
|
||||
const html_webpack_plugin_1 = __importDefault(require("html-webpack-plugin"));
|
||||
const webpack_1 = require("webpack");
|
||||
const node_path_1 = __importDefault(require("node:path"));
|
||||
const setModuleFederationPlugin_1 = require("./setModuleFederationPlugin");
|
||||
/**
|
||||
* Установка плагинов.
|
||||
*/
|
||||
const setPlugins = ({ isDevelopmentMode, isProductionMode, moduleName, publicPath, moduleFederationOptions, }) => {
|
||||
const dotenvFilename = isProductionMode ? '.env.production' : '.env.development';
|
||||
const plugins = [
|
||||
new dotenv_webpack_1.default({
|
||||
path: dotenvFilename,
|
||||
}),
|
||||
new html_webpack_plugin_1.default({
|
||||
template: node_path_1.default.resolve(publicPath, 'index.html'),
|
||||
filename: 'index.html',
|
||||
favicon: node_path_1.default.resolve(publicPath, 'favicon.ico'),
|
||||
hash: true,
|
||||
cache: true,
|
||||
}),
|
||||
(0, setModuleFederationPlugin_1.setModuleFederationPlugin)({ moduleName, moduleFederationOptions }),
|
||||
];
|
||||
if (isDevelopmentMode) {
|
||||
plugins.push(new webpack_1.ProgressPlugin(),
|
||||
// Выносит проверку типов в отдельный процесс
|
||||
new fork_ts_checker_webpack_plugin_1.default({
|
||||
typescript: {
|
||||
diagnosticOptions: {
|
||||
semantic: true,
|
||||
syntactic: true,
|
||||
},
|
||||
},
|
||||
}), new react_refresh_webpack_plugin_1.default());
|
||||
}
|
||||
// @ts-ignore
|
||||
return plugins;
|
||||
};
|
||||
exports.setPlugins = setPlugins;
|
||||
@@ -0,0 +1,20 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.normalizePackageName = void 0;
|
||||
__exportStar(require("./types"), exports);
|
||||
var utils_1 = require("./utils");
|
||||
Object.defineProperty(exports, "normalizePackageName", { enumerable: true, get: function () { return utils_1.normalizePackageName; } });
|
||||
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
@@ -0,0 +1,8 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createFileName = void 0;
|
||||
function createFileName({ directoryType, isProductionMode }) {
|
||||
const fileSuffix = isProductionMode ? '.[contenthash:8]' : '';
|
||||
return [directoryType, '/', '[name]', fileSuffix, '[ext]'].join('');
|
||||
}
|
||||
exports.createFileName = createFileName;
|
||||
@@ -0,0 +1,9 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createFileName = exports.readConfigFile = exports.normalizePackageName = void 0;
|
||||
var normalizePackageName_1 = require("./normalizePackageName");
|
||||
Object.defineProperty(exports, "normalizePackageName", { enumerable: true, get: function () { return normalizePackageName_1.normalizePackageName; } });
|
||||
var readConfigFile_1 = require("./readConfigFile");
|
||||
Object.defineProperty(exports, "readConfigFile", { enumerable: true, get: function () { return readConfigFile_1.readConfigFile; } });
|
||||
var createFileName_1 = require("./createFileName");
|
||||
Object.defineProperty(exports, "createFileName", { enumerable: true, get: function () { return createFileName_1.createFileName; } });
|
||||
@@ -0,0 +1,6 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.normalizePackageName = void 0;
|
||||
// Функция которая преобразует название пакета для совместимости с ModuleFederationPlugin.
|
||||
const normalizePackageName = (packageName) => packageName.replace(/-/g, '_').toLowerCase();
|
||||
exports.normalizePackageName = normalizePackageName;
|
||||
@@ -0,0 +1,20 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.readConfigFile = void 0;
|
||||
const node_fs_1 = __importDefault(require("node:fs"));
|
||||
const node_path_1 = __importDefault(require("node:path"));
|
||||
const requireTs_1 = require("./requireTs");
|
||||
function readConfigFile(configPath = node_path_1.default.resolve(process.cwd(), 'webpack.config.ts')) {
|
||||
if (!node_fs_1.default.existsSync(configPath)) {
|
||||
return undefined;
|
||||
}
|
||||
if (configPath.endsWith('.ts')) {
|
||||
return (0, requireTs_1.requireTs)(configPath).default;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
return require(configPath).default;
|
||||
}
|
||||
exports.readConfigFile = readConfigFile;
|
||||
@@ -0,0 +1,17 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.requireTs = void 0;
|
||||
const ts_node_1 = require("ts-node");
|
||||
let tsNodeRegistered = false;
|
||||
function requireTs(path) {
|
||||
if (!tsNodeRegistered) {
|
||||
(0, ts_node_1.register)({
|
||||
compilerOptions: {
|
||||
module: 'CommonJS',
|
||||
},
|
||||
});
|
||||
tsNodeRegistered = true;
|
||||
}
|
||||
return require(path);
|
||||
}
|
||||
exports.requireTs = requireTs;
|
||||
@@ -0,0 +1,99 @@
|
||||
{
|
||||
"name": "@msb/mf-builder",
|
||||
"version": "1.0.0",
|
||||
"description": "CLI для сборки модулей с помощью Webpack 5",
|
||||
"main": "lib/index.js",
|
||||
"types": "types/index.d.ts",
|
||||
"files": ["bin", "lib", "types"],
|
||||
"bin": {
|
||||
"build-app": "bin/build-app.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc && tsc-alias",
|
||||
"check-types": "tsc --emitDeclarationOnly"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/code-frame": "7.18.6",
|
||||
"@babel/compat-data": "7.21.0",
|
||||
"@babel/core": "7.18.6",
|
||||
"@babel/generator": "7.21.1",
|
||||
"@babel/helper-annotate-as-pure": "7.18.6",
|
||||
"@babel/helper-compilation-targets": "7.20.7",
|
||||
"@babel/helper-create-class-features-plugin": "7.21.0",
|
||||
"@babel/helper-create-regexp-features-plugin": "7.21.0",
|
||||
"@babel/helper-environment-visitor": "7.18.9",
|
||||
"@babel/helper-function-name": "7.21.0",
|
||||
"@babel/helper-member-expression-to-functions": "7.21.0",
|
||||
"@babel/helper-module-transforms": "7.21.2",
|
||||
"@babel/helper-plugin-utils": "7.20.2",
|
||||
"@babel/helper-replace-supers": "7.20.7",
|
||||
"@babel/helper-simple-access": "7.20.2",
|
||||
"@babel/helper-string-parser": "7.19.4",
|
||||
"@babel/helper-validator-identifier": "7.19.1",
|
||||
"@babel/helper-validator-option": "7.21.0",
|
||||
"@babel/helper-wrap-function": "7.20.5",
|
||||
"@babel/helpers": "7.21.0",
|
||||
"@babel/highlight": "7.18.6",
|
||||
"@babel/parser": "7.21.2",
|
||||
"@babel/plugin-proposal-async-generator-functions": "7.20.7",
|
||||
"@babel/plugin-proposal-decorators": "7.21.0",
|
||||
"@babel/plugin-proposal-private-property-in-object": "7.21.0",
|
||||
"@babel/plugin-syntax-decorators": "7.21.0",
|
||||
"@babel/plugin-syntax-jsx": "7.18.6",
|
||||
"@babel/plugin-transform-block-scoping": "7.21.0",
|
||||
"@babel/plugin-transform-classes": "7.21.0",
|
||||
"@babel/plugin-transform-flow-strip-types": "7.21.0",
|
||||
"@babel/plugin-transform-for-of": "7.21.0",
|
||||
"@babel/plugin-transform-modules-amd": "7.20.11",
|
||||
"@babel/plugin-transform-modules-commonjs": "7.21.2",
|
||||
"@babel/plugin-transform-object-super": "7.18.6",
|
||||
"@babel/plugin-transform-react-jsx": "7.21.0",
|
||||
"@babel/plugin-transform-react-jsx-self": "7.21.0",
|
||||
"@babel/plugin-transform-typescript": "7.21.0",
|
||||
"@babel/preset-env": "7.16.11",
|
||||
"@babel/preset-react": "7.9.4",
|
||||
"@babel/preset-typescript": "7.21.0",
|
||||
"@babel/register": "7.21.0",
|
||||
"@babel/runtime": "7.21.0",
|
||||
"@babel/template": "7.20.7",
|
||||
"@babel/traverse": "7.23.2",
|
||||
"@babel/types": "7.21.2",
|
||||
"@eco/eslint-config": "^21.10.0",
|
||||
"@eco/eslint-plugin": "^21.7.0",
|
||||
"@eco/prettier-config": "21.10.0",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "0.5.11",
|
||||
"@types/dotenv-webpack": "^1.8.0",
|
||||
"@types/eslint": "8.21.1",
|
||||
"@types/node": "22.3.0",
|
||||
"@types/webpack": "5.28.5",
|
||||
"@types/yargs": "17.0.33",
|
||||
"babel-loader": "8.2.5",
|
||||
"babel-plugin-styled-components": "2.0.7",
|
||||
"chalk": "5.4.1",
|
||||
"circular-dependency-plugin": "5.2.2",
|
||||
"clean-webpack-plugin": "4.0.0",
|
||||
"copy-webpack-plugin": "10.2.4",
|
||||
"cross-env": "7.0.3",
|
||||
"css-loader": "6.7.0",
|
||||
"css-minimizer-webpack-plugin": "3.4.1",
|
||||
"dotenv-webpack": "7.1.0",
|
||||
"eslint": "^8.9.0",
|
||||
"file-loader": "6.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "7.1.0",
|
||||
"html-webpack-plugin": "5.6.3",
|
||||
"mini-css-extract-plugin": "2.6.0",
|
||||
"prettier": "2.5.1",
|
||||
"style-loader": "3.3.4",
|
||||
"terser-webpack-plugin": "5.2.4",
|
||||
"ts-node": "10.9.2",
|
||||
"tsc-alias": "1.8.15",
|
||||
"typescript": "5.4.5",
|
||||
"webpack": "5.97.1",
|
||||
"webpack-clean": "1.2.5",
|
||||
"webpack-cleanup-plugin": "0.5.1",
|
||||
"webpack-cli": "4.10.0",
|
||||
"webpack-dev-server": "4.9.2",
|
||||
"webpack-merge": "5.10.0",
|
||||
"yargs": "17.7.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { createWebpackConfig } from 'config';
|
||||
import type { Mode, IWebpackAppConfig } from 'types';
|
||||
import { readConfigFile } from 'utils';
|
||||
import webpack from 'webpack';
|
||||
import type { Stats } from 'webpack';
|
||||
|
||||
interface IBuildOptions {
|
||||
configPath?: string;
|
||||
mode: Mode;
|
||||
}
|
||||
|
||||
function build({ configPath, mode }: IBuildOptions): Promise<Stats | undefined> {
|
||||
const appConfig = (readConfigFile(configPath) ?? {}) as IWebpackAppConfig;
|
||||
|
||||
const config = createWebpackConfig(appConfig, mode);
|
||||
|
||||
const compiler = webpack(config);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
compiler.run((error, stats) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
}
|
||||
|
||||
if (stats) {
|
||||
console.log(stats.toString());
|
||||
}
|
||||
|
||||
resolve(stats);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default build;
|
||||
@@ -0,0 +1,3 @@
|
||||
export { default as start } from './startDevServer';
|
||||
|
||||
export { default as build } from './buildMode';
|
||||
@@ -0,0 +1,31 @@
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
import { createWebpackConfig } from 'config';
|
||||
import type { IWebpackAppConfig } from 'types';
|
||||
import { readConfigFile } from 'utils';
|
||||
import webpack from 'webpack';
|
||||
import WebpackDevServer from 'webpack-dev-server';
|
||||
|
||||
interface IStartOptions {
|
||||
configPath?: string;
|
||||
}
|
||||
|
||||
function start({ configPath }: IStartOptions) {
|
||||
const appConfig = (readConfigFile(configPath) ?? {}) as IWebpackAppConfig;
|
||||
|
||||
const { devServer, ...config } = createWebpackConfig(appConfig);
|
||||
|
||||
const compiler = webpack(config);
|
||||
|
||||
const developmentServer = new WebpackDevServer(devServer, compiler);
|
||||
|
||||
developmentServer.startCallback(() => {
|
||||
process.stdin.on('end', () => {
|
||||
// @ts-ignore
|
||||
developmentServer.close();
|
||||
process.exit();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default start;
|
||||
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env node
|
||||
/* eslint-disable sonarjs/no-identical-functions */
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
|
||||
import yargs from 'yargs';
|
||||
import path from 'node:path';
|
||||
|
||||
import { build, start } from './commands';
|
||||
|
||||
const DEFAULT_CONFIG_FILE_PATH = path.resolve('webpack.config.ts');
|
||||
|
||||
yargs
|
||||
.scriptName('build-app')
|
||||
.command(
|
||||
['build'],
|
||||
'Собрать приложение в production режиме',
|
||||
command => {
|
||||
command.option('config', {
|
||||
alias: 'c',
|
||||
default: DEFAULT_CONFIG_FILE_PATH,
|
||||
type: 'string',
|
||||
description: 'Путь до конфигурационного файла webpack.config.ts',
|
||||
});
|
||||
},
|
||||
argv => {
|
||||
build({ configPath: argv.config as string, mode: 'production' });
|
||||
}
|
||||
)
|
||||
.command(
|
||||
['build:dev'],
|
||||
'Собрать приложение в development режиме',
|
||||
command => {
|
||||
command.option('config', {
|
||||
alias: 'c',
|
||||
default: DEFAULT_CONFIG_FILE_PATH,
|
||||
type: 'string',
|
||||
description: 'Путь до конфигурационного файла webpack.config.ts',
|
||||
});
|
||||
},
|
||||
argv => {
|
||||
build({ configPath: argv.config as string, mode: 'development' });
|
||||
}
|
||||
)
|
||||
.command(
|
||||
['start'],
|
||||
'Запустить webpack-dev-server',
|
||||
command => {
|
||||
command.option('config', {
|
||||
alias: 'c',
|
||||
default: DEFAULT_CONFIG_FILE_PATH,
|
||||
type: 'string',
|
||||
description: 'Путь до конфигурационного файла webpack.config.ts',
|
||||
});
|
||||
},
|
||||
argv => {
|
||||
start({ configPath: argv.config as string });
|
||||
}
|
||||
)
|
||||
.help().argv;
|
||||
@@ -0,0 +1 @@
|
||||
export { removeDataTestIdBabelPlugin } from './removeDataTestidPlugin';
|
||||
@@ -0,0 +1,25 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
import type { PluginItem } from '@babel/core';
|
||||
|
||||
/**
|
||||
* Плагин для удаления указанных атрибутов из верстки.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export const removeDataTestIdBabelPlugin = (): PluginItem => ({
|
||||
visitor: {
|
||||
Program(path, state) {
|
||||
const forbiddenProps = state.opts.props || [];
|
||||
|
||||
path.traverse({
|
||||
JSXIdentifier(current) {
|
||||
const nodeName = current.node.name;
|
||||
|
||||
if (forbiddenProps.includes(nodeName)) {
|
||||
current.parentPath.remove();
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
export * from './webpack';
|
||||
@@ -0,0 +1,128 @@
|
||||
import type { IWebpackAppConfig, Mode } from 'types';
|
||||
import type { Configuration } from 'webpack';
|
||||
import { merge } from 'webpack-merge';
|
||||
import path from 'node:path';
|
||||
import { setDevServer } from './setDevServer';
|
||||
import { setModuleOptions } from './setModuleOptions';
|
||||
import { setOptimizationRules } from './setOptimizationRules';
|
||||
import { setPlugins } from './setPlugins';
|
||||
|
||||
export class WebpackConfigBuilder {
|
||||
private config: Configuration = {};
|
||||
private appConfig: IWebpackAppConfig;
|
||||
private isDevelopmentMode: boolean;
|
||||
private isProductionMode: boolean;
|
||||
private cwd: string;
|
||||
private srcPath: string;
|
||||
|
||||
constructor(appConfig: IWebpackAppConfig, mode: Mode) {
|
||||
this.appConfig = appConfig;
|
||||
this.isDevelopmentMode = mode === 'development';
|
||||
this.isProductionMode = mode === 'production';
|
||||
this.cwd = process.cwd();
|
||||
this.srcPath = path.resolve(this.cwd, appConfig.paths?.srcPath || 'src');
|
||||
|
||||
const entry = appConfig.paths?.entry || path.resolve(this.srcPath, 'index.ts');
|
||||
const target = this.isDevelopmentMode ? 'web' : 'browserslist';
|
||||
const devtool = this.isDevelopmentMode ? 'eval-cheap-module-source-map' : 'source-map';
|
||||
|
||||
this.config = {
|
||||
mode,
|
||||
target,
|
||||
devtool,
|
||||
entry,
|
||||
};
|
||||
}
|
||||
|
||||
public get configuration() {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
public applyOutput() {
|
||||
const port = this.appConfig.devServerOptions?.port;
|
||||
|
||||
this.config.output = {
|
||||
path: this.appConfig.paths?.outputPath || path.resolve(this.cwd, 'dist'),
|
||||
filename: '[name].[contenthash:8].js',
|
||||
chunkFilename: '[name].[contenthash:8].js',
|
||||
publicPath: this.isDevelopmentMode && port ? `http://localhost:${port}/` : this.appConfig.paths?.publicUrl || '/',
|
||||
clean: true,
|
||||
};
|
||||
}
|
||||
|
||||
public applyResolve() {
|
||||
this.config.resolve = {
|
||||
alias: {
|
||||
// FSD alias
|
||||
'@/app': path.resolve(this.srcPath, 'app'),
|
||||
'@/shared': path.resolve(this.srcPath, 'shared'),
|
||||
'@/pages': path.resolve(this.srcPath, 'pages'),
|
||||
'@/widgets': path.resolve(this.srcPath, 'widgets'),
|
||||
'@/features': path.resolve(this.srcPath, 'features'),
|
||||
'@/entities': path.resolve(this.srcPath, 'entities'),
|
||||
|
||||
// jsx-runtime
|
||||
'react/jsx-dev-runtime': 'react/jsx-dev-runtime.js',
|
||||
'react/jsx-runtime': 'react/jsx-runtime.js',
|
||||
},
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
||||
};
|
||||
}
|
||||
|
||||
public applyDevServer() {
|
||||
if (this.isDevelopmentMode) {
|
||||
const devServerOptions = {
|
||||
...this.appConfig.devServerOptions,
|
||||
port: this.appConfig.devServerOptions?.port || 3001,
|
||||
};
|
||||
|
||||
this.config.devServer = setDevServer(devServerOptions);
|
||||
}
|
||||
}
|
||||
|
||||
public applyPerfomance() {
|
||||
if (this.isProductionMode) {
|
||||
this.config.performance = {
|
||||
hints: false,
|
||||
maxEntrypointSize: 512_000,
|
||||
maxAssetSize: 512_000,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public applyOptimizationRules() {
|
||||
if (this.isProductionMode) {
|
||||
this.config.optimization = setOptimizationRules(this.appConfig.optimizationOptions);
|
||||
}
|
||||
}
|
||||
|
||||
public applyModuleOptions() {
|
||||
this.config.module = setModuleOptions({
|
||||
isDevelopmentMode: this.isDevelopmentMode,
|
||||
isProductionMode: this.isProductionMode,
|
||||
});
|
||||
}
|
||||
|
||||
public applyPlugins() {
|
||||
const publicPath = this.appConfig.paths?.publicPath || 'public';
|
||||
|
||||
this.config.plugins =
|
||||
setPlugins({
|
||||
isDevelopmentMode: this.isDevelopmentMode,
|
||||
isProductionMode: this.isProductionMode,
|
||||
publicPath,
|
||||
moduleName: this.appConfig.moduleName,
|
||||
moduleFederationOptions: this.appConfig.moduleFederationOptions,
|
||||
}) || [];
|
||||
|
||||
const otherPlugins = this.appConfig.plugins;
|
||||
|
||||
if (Array.isArray(otherPlugins) && Array.isArray(this.config.plugins)) {
|
||||
this.config.plugins.concat(otherPlugins);
|
||||
}
|
||||
}
|
||||
|
||||
public merge(newConfig: Configuration) {
|
||||
merge(this.config, newConfig);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { IWebpackAppConfig, Mode } from 'types';
|
||||
import type { Configuration } from 'webpack';
|
||||
import { WebpackConfigBuilder } from './WebpackConfigBuilder';
|
||||
|
||||
/**
|
||||
* Создание конфига для сборки.
|
||||
*/
|
||||
export const createWebpackConfig = (environmentConfig: IWebpackAppConfig, mode: Mode = 'development'): Configuration => {
|
||||
const builder = new WebpackConfigBuilder(environmentConfig, mode);
|
||||
|
||||
builder.applyOutput();
|
||||
builder.applyDevServer();
|
||||
builder.applyModuleOptions();
|
||||
builder.applyOptimizationRules();
|
||||
builder.applyPerfomance();
|
||||
builder.applyResolve();
|
||||
builder.applyPlugins();
|
||||
|
||||
return builder.configuration;
|
||||
};
|
||||
|
||||
export default createWebpackConfig;
|
||||
@@ -0,0 +1 @@
|
||||
export { createWebpackConfig } from './createWebpackConfig';
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { Configuration } from 'webpack-dev-server';
|
||||
|
||||
/**
|
||||
* Настройка Webpack Dev Server.
|
||||
*/
|
||||
export const setDevServer = (devServerOptions: Configuration): Configuration => ({
|
||||
open: false,
|
||||
hot: true,
|
||||
historyApiFallback: true,
|
||||
client: {
|
||||
overlay: false,
|
||||
},
|
||||
...devServerOptions,
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
import type { IPluginOptions } from 'types';
|
||||
import { normalizePackageName } from 'utils';
|
||||
import { container } from 'webpack';
|
||||
|
||||
/**
|
||||
* Настройка плагина ModuleFederation.
|
||||
*/
|
||||
export const setModuleFederationPlugin = ({
|
||||
moduleName,
|
||||
moduleFederationOptions,
|
||||
}: Pick<IPluginOptions, 'moduleFederationOptions' | 'moduleName'>) =>
|
||||
new container.ModuleFederationPlugin({
|
||||
name: normalizePackageName(moduleName),
|
||||
filename: 'remoteEntry.js',
|
||||
shared: {
|
||||
react: {
|
||||
singleton: true,
|
||||
eager: true,
|
||||
},
|
||||
'react-dom': {
|
||||
singleton: true,
|
||||
eager: true,
|
||||
},
|
||||
'styled-components': {
|
||||
singleton: true,
|
||||
eager: true,
|
||||
},
|
||||
},
|
||||
...moduleFederationOptions,
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
import type { PluginItem } from '@babel/core';
|
||||
import type { IModeOptions } from 'types';
|
||||
import { createFileName } from 'utils';
|
||||
import type { Configuration } from 'webpack';
|
||||
import { removeDataTestIdBabelPlugin } from '../babel';
|
||||
|
||||
/**
|
||||
* Настройка обработки разных типов модулей.
|
||||
*/
|
||||
export const setModuleOptions = ({ isDevelopmentMode, isProductionMode }: IModeOptions): Configuration['module'] => {
|
||||
const babelPlugins: Array<PluginItem | string> = ['babel-plugin-styled-components'];
|
||||
|
||||
if (isDevelopmentMode) {
|
||||
babelPlugins.push(require.resolve('react-refresh/babel'));
|
||||
}
|
||||
|
||||
if (isProductionMode) {
|
||||
babelPlugins.push([
|
||||
removeDataTestIdBabelPlugin,
|
||||
{
|
||||
props: ['data-testid'],
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
return {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(png|svg|jpg|jpeg|gif|woff|woff2|eot|ttf|otf)$/i,
|
||||
type: 'asset/resource',
|
||||
generator: {
|
||||
filename: createFileName({ directoryType: 'assets', isProductionMode }),
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.(css)$/,
|
||||
use: ['style-loader', 'css-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.(ts|js)x?$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: ['@babel/preset-env', ['@babel/preset-react', { runtime: 'automatic' }], '@babel/preset-typescript'],
|
||||
plugins: babelPlugins,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import TerserPlugin from 'terser-webpack-plugin';
|
||||
import type { Configuration } from 'webpack';
|
||||
|
||||
/**
|
||||
* Установка правил оптимизации сборки.
|
||||
*/
|
||||
export const setOptimizationRules = (options?: Configuration['optimization']): Configuration['optimization'] => ({
|
||||
minimize: true,
|
||||
minimizer: [new TerserPlugin()],
|
||||
splitChunks: {
|
||||
chunks: 'all',
|
||||
maxInitialRequests: Number.POSITIVE_INFINITY,
|
||||
minSize: 0,
|
||||
cacheGroups: {
|
||||
vendor: {
|
||||
test: /[/\\]node_modules[/\\]/,
|
||||
name(module: { context: string }) {
|
||||
const packageName = module.context.match(/[/\\]node_modules[/\\](.*?)([/\\]|$)/) ?? 'package';
|
||||
|
||||
return `npm.${packageName[1].replace('@', '')}`;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
...options,
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
|
||||
import Dotenv from 'dotenv-webpack';
|
||||
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
|
||||
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
||||
import type { IPluginOptions } from 'types';
|
||||
import { ProgressPlugin } from 'webpack';
|
||||
import type { Configuration } from 'webpack';
|
||||
import path from 'node:path';
|
||||
import { setModuleFederationPlugin } from './setModuleFederationPlugin';
|
||||
|
||||
/**
|
||||
* Установка плагинов.
|
||||
*/
|
||||
export const setPlugins = ({
|
||||
isDevelopmentMode,
|
||||
isProductionMode,
|
||||
moduleName,
|
||||
publicPath,
|
||||
moduleFederationOptions,
|
||||
}: IPluginOptions): Configuration['plugins'] => {
|
||||
const dotenvFilename = isProductionMode ? '.env.production' : '.env.development';
|
||||
const plugins = [
|
||||
new Dotenv({
|
||||
path: dotenvFilename,
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
template: path.resolve(publicPath, 'index.html'),
|
||||
filename: 'index.html',
|
||||
favicon: path.resolve(publicPath, 'favicon.ico'),
|
||||
hash: true,
|
||||
cache: true,
|
||||
}),
|
||||
setModuleFederationPlugin({ moduleName, moduleFederationOptions }),
|
||||
];
|
||||
|
||||
if (isDevelopmentMode) {
|
||||
plugins.push(
|
||||
new ProgressPlugin(),
|
||||
// Выносит проверку типов в отдельный процесс
|
||||
new ForkTsCheckerWebpackPlugin({
|
||||
typescript: {
|
||||
diagnosticOptions: {
|
||||
semantic: true,
|
||||
syntactic: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
new ReactRefreshWebpackPlugin()
|
||||
);
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
return plugins;
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './types';
|
||||
export { normalizePackageName } from './utils';
|
||||
@@ -0,0 +1,49 @@
|
||||
/* eslint-disable jsdoc/require-description-complete-sentence */
|
||||
import type { Configuration } from 'webpack';
|
||||
import type { Configuration as DevServerConfiguration } from 'webpack-dev-server';
|
||||
import type { IModuleFederationPluginOptions } from './moduleFederation';
|
||||
|
||||
export type Mode = 'development' | 'production';
|
||||
|
||||
export interface IPaths {
|
||||
/** Точка входа приложения */
|
||||
entry: Configuration['entry'];
|
||||
/** Абсолютный или относительный пути к файлам */
|
||||
publicUrl: string;
|
||||
/** Путь до папки, в которой находятся публично доступные файлы */
|
||||
publicPath: string;
|
||||
/** Путь до папки, в которой будет кладется бандл */
|
||||
outputPath: string;
|
||||
/** Путь до папки с исходным кодом */
|
||||
srcPath: string;
|
||||
}
|
||||
|
||||
export interface IModeOptions {
|
||||
isDevelopmentMode: boolean;
|
||||
isProductionMode: boolean;
|
||||
}
|
||||
|
||||
export interface IPluginOptions extends IModeOptions {
|
||||
/** Название модуля */
|
||||
moduleName: string;
|
||||
/** Режим сборки. */
|
||||
publicPath: string;
|
||||
/** Конфигурация для ModuleFedrationPlugin. */
|
||||
moduleFederationOptions?: IModuleFederationPluginOptions;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface IWebpackAppConfig {
|
||||
/** Название модуля */
|
||||
moduleName: string;
|
||||
/** Переопределение путей */
|
||||
paths?: Partial<IPaths>;
|
||||
/** Конфигурация для webpack dev server */
|
||||
devServerOptions?: DevServerConfiguration;
|
||||
/** Конфигурация для оптимизации сборки */
|
||||
optimizationOptions?: Configuration['optimization'];
|
||||
/** Конфигурация для ModuleFedrationPlugin */
|
||||
moduleFederationOptions?: IModuleFederationPluginOptions;
|
||||
/** Плагины */
|
||||
plugins?: Configuration['plugins'];
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
export interface IModuleFederationPluginOptions {
|
||||
/**
|
||||
* Modules that should be exposed by this container. When provided, property name is used as public name, otherwise public name is automatically inferred from request.
|
||||
*/
|
||||
exposes?: Array<ExposesObject | string> | ExposesObject;
|
||||
|
||||
/**
|
||||
* The filename of the container as relative path inside the `output.path` directory.
|
||||
*/
|
||||
filename?: string;
|
||||
|
||||
/**
|
||||
* Options for library.
|
||||
*/
|
||||
library?: ILibraryOptions;
|
||||
|
||||
/**
|
||||
* The name of the container.
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* The external type of the remote containers.
|
||||
*/
|
||||
remoteType?:
|
||||
| 'amd-require'
|
||||
| 'amd'
|
||||
| 'assign'
|
||||
| 'commonjs-module'
|
||||
| 'commonjs-static'
|
||||
| 'commonjs'
|
||||
| 'commonjs2'
|
||||
| 'global'
|
||||
| 'import'
|
||||
| 'jsonp'
|
||||
| 'module-import'
|
||||
| 'module'
|
||||
| 'node-commonjs'
|
||||
| 'promise'
|
||||
| 'script'
|
||||
| 'self'
|
||||
| 'system'
|
||||
| 'this'
|
||||
| 'umd'
|
||||
| 'umd2'
|
||||
| 'var'
|
||||
| 'window';
|
||||
|
||||
/**
|
||||
* Container locations and request scopes from which modules should be resolved and loaded at runtime. When provided, property name is used as request scope, otherwise request scope is automatically inferred from container location.
|
||||
*/
|
||||
remotes?: Array<RemotesObject | string> | RemotesObject;
|
||||
|
||||
/**
|
||||
* The name of the runtime chunk. If set a runtime chunk with this name is created or an existing entrypoint is used as runtime.
|
||||
*/
|
||||
runtime?: string | false;
|
||||
|
||||
/**
|
||||
* Share scope name used for all shared modules (defaults to 'default').
|
||||
*/
|
||||
shareScope?: string;
|
||||
|
||||
/**
|
||||
* Modules that should be shared in the share scope. When provided, property names are used to match requested modules in this compilation.
|
||||
*/
|
||||
shared?: Array<SharedObject | string> | SharedObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for library.
|
||||
*/
|
||||
interface ILibraryOptions {
|
||||
/**
|
||||
* Add a container for define/require functions in the AMD module.
|
||||
*/
|
||||
amdContainer?: string;
|
||||
|
||||
/**
|
||||
* Add a comment in the UMD wrapper.
|
||||
*/
|
||||
auxiliaryComment?: string;
|
||||
|
||||
/**
|
||||
* Specify which export should be exposed as library.
|
||||
*/
|
||||
export?: string[] | string;
|
||||
|
||||
/**
|
||||
* The name of the library (some types allow unnamed libraries too).
|
||||
*/
|
||||
name?: string[] | string;
|
||||
|
||||
/**
|
||||
* Type of library (types included by default are 'var', 'module', 'assign', 'assign-properties', 'this', 'window', 'self', 'global', 'commonjs', 'commonjs2', 'commonjs-module', 'commonjs-static', 'amd', 'amd-require', 'umd', 'umd2', 'jsonp', 'system', but others might be added by plugins).
|
||||
*/
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* If `output.libraryTarget` is set to umd and `output.library` is set, setting this to true will name the AMD module.
|
||||
*/
|
||||
umdNamedDefine?: boolean;
|
||||
}
|
||||
/**
|
||||
* Container locations from which modules should be resolved and loaded at runtime. Property names are used as request scopes.
|
||||
*/
|
||||
type RemotesObject = Record<string, string[] | string>;
|
||||
|
||||
interface IExposesConfig {
|
||||
/**
|
||||
* Request to a module that should be exposed by this container.
|
||||
*/
|
||||
import: string[] | string;
|
||||
|
||||
/**
|
||||
* Custom chunk name for the exposed module.
|
||||
*/
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Advanced configuration for modules that should be exposed by this container.
|
||||
*/
|
||||
interface IExposesConfig {
|
||||
/**
|
||||
* Request to a module that should be exposed by this container.
|
||||
*/
|
||||
import: string[] | string;
|
||||
|
||||
/**
|
||||
* Custom chunk name for the exposed module.
|
||||
*/
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modules that should be exposed by this container. Property names are used as public paths.
|
||||
*/
|
||||
type ExposesObject = Record<string, IExposesConfig | string[] | string>;
|
||||
|
||||
/**
|
||||
* Advanced configuration for modules that should be shared in the share scope.
|
||||
*/
|
||||
interface ISharedConfig {
|
||||
/**
|
||||
* Include the provided and fallback module directly instead behind an async request. This allows to use this shared module in initial load too. All possible shared modules need to be eager too.
|
||||
*/
|
||||
eager?: boolean;
|
||||
|
||||
/**
|
||||
* Provided module that should be provided to share scope. Also acts as fallback module if no shared module is found in share scope or version isn't valid. Defaults to the property name.
|
||||
*/
|
||||
import?: string | false;
|
||||
|
||||
/**
|
||||
* Package name to determine required version from description file. This is only needed when package name can't be automatically determined from request.
|
||||
*/
|
||||
packageName?: string;
|
||||
|
||||
/**
|
||||
* Version requirement from module in share scope.
|
||||
*/
|
||||
requiredVersion?: string | false;
|
||||
|
||||
/**
|
||||
* Module is looked up under this key from the share scope.
|
||||
*/
|
||||
shareKey?: string;
|
||||
|
||||
/**
|
||||
* Share scope name.
|
||||
*/
|
||||
shareScope?: string;
|
||||
|
||||
/**
|
||||
* Allow only a single version of the shared module in share scope (disabled by default).
|
||||
*/
|
||||
singleton?: boolean;
|
||||
|
||||
/**
|
||||
* Do not accept shared module if version is not valid (defaults to yes, if local fallback module is available and shared module is not a singleton, otherwise no, has no effect if there is no required version specified).
|
||||
*/
|
||||
strictVersion?: boolean;
|
||||
|
||||
/**
|
||||
* Version of the provided module. Will replace lower matching versions, but not higher.
|
||||
*/
|
||||
version?: string | false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modules that should be shared in the share scope. Property names are used to match requested modules in this compilation. Relative requests are resolved, module requests are matched unresolved, absolute paths will match resolved requests. A trailing slash will match all requests with this prefix. In this case shareKey must also have a trailing slash.
|
||||
*/
|
||||
type SharedObject = Record<string, ISharedConfig | string>;
|
||||
@@ -0,0 +1,10 @@
|
||||
export interface ICreateFileNameOptions {
|
||||
directoryType: string;
|
||||
isProductionMode: boolean;
|
||||
}
|
||||
|
||||
export function createFileName({ directoryType, isProductionMode }: ICreateFileNameOptions): string {
|
||||
const fileSuffix = isProductionMode ? '.[contenthash:8]' : '';
|
||||
|
||||
return [directoryType, '/', '[name]', fileSuffix, '[ext]'].join('');
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export { normalizePackageName } from './normalizePackageName';
|
||||
export { readConfigFile } from './readConfigFile';
|
||||
export { createFileName } from './createFileName';
|
||||
@@ -0,0 +1,2 @@
|
||||
// Функция которая преобразует название пакета для совместимости с ModuleFederationPlugin.
|
||||
export const normalizePackageName = (packageName: string): string => packageName.replace(/-/g, '_').toLowerCase();
|
||||
@@ -0,0 +1,17 @@
|
||||
import type { IWebpackAppConfig } from 'types';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { requireTs } from './requireTs';
|
||||
|
||||
export function readConfigFile(configPath: string = path.resolve(process.cwd(), 'webpack.config.ts')): IWebpackAppConfig | undefined {
|
||||
if (!fs.existsSync(configPath)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (configPath.endsWith('.ts')) {
|
||||
return requireTs<{ default: IWebpackAppConfig }>(configPath).default;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
return require(configPath).default;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { register } from 'ts-node';
|
||||
|
||||
let tsNodeRegistered = false;
|
||||
|
||||
export function requireTs<M = unknown>(path: string): M {
|
||||
if (!tsNodeRegistered) {
|
||||
register({
|
||||
compilerOptions: {
|
||||
module: 'CommonJS',
|
||||
},
|
||||
});
|
||||
|
||||
tsNodeRegistered = true;
|
||||
}
|
||||
|
||||
return require(path);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"outDir": "lib",
|
||||
"declarationDir": "types",
|
||||
"module": "CommonJS",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"lib": ["ESNext"],
|
||||
"target": "ES2017",
|
||||
"esModuleInterop": true,
|
||||
"moduleResolution": "node",
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"cli": ["src/cli"],
|
||||
"utils": ["src/utils"],
|
||||
"config": ["src/config"],
|
||||
"types": ["src/types"]
|
||||
}
|
||||
},
|
||||
"include": ["src", ".eslintrc.js"],
|
||||
"exclude": ["node_modules", "lib", "types"]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { Mode } from '../../types';
|
||||
import type { Stats } from 'webpack';
|
||||
interface IBuildOptions {
|
||||
configPath?: string;
|
||||
mode: Mode;
|
||||
}
|
||||
declare function build({ configPath, mode }: IBuildOptions): Promise<Stats | undefined>;
|
||||
export default build;
|
||||
//# sourceMappingURL=buildMode.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"buildMode.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/buildMode.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAqB,MAAM,OAAO,CAAC;AAGrD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,UAAU,aAAa;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,iBAAS,KAAK,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,aAAa,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAoB9E;AAED,eAAe,KAAK,CAAC"}
|
||||
@@ -0,0 +1,3 @@
|
||||
export { default as start } from './startDevServer';
|
||||
export { default as build } from './buildMode';
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,aAAa,CAAC"}
|
||||
@@ -0,0 +1,6 @@
|
||||
interface IStartOptions {
|
||||
configPath?: string;
|
||||
}
|
||||
declare function start({ configPath }: IStartOptions): void;
|
||||
export default start;
|
||||
//# sourceMappingURL=startDevServer.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"startDevServer.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/startDevServer.ts"],"names":[],"mappings":"AAQA,UAAU,aAAa;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,iBAAS,KAAK,CAAC,EAAE,UAAU,EAAE,EAAE,aAAa,QAgB3C;AAED,eAAe,KAAK,CAAC"}
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
export {};
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { removeDataTestIdBabelPlugin } from './removeDataTestidPlugin';
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/config/babel/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC"}
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { PluginItem } from '@babel/core';
|
||||
/**
|
||||
* Плагин для удаления указанных атрибутов из верстки.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export declare const removeDataTestIdBabelPlugin: () => PluginItem;
|
||||
//# sourceMappingURL=removeDataTestidPlugin.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"removeDataTestidPlugin.d.ts","sourceRoot":"","sources":["../../../src/config/babel/removeDataTestidPlugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,QAAO,UAgB7C,CAAC"}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './webpack';
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC"}
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { IWebpackAppConfig, Mode } from '../../types';
|
||||
import type { Configuration } from 'webpack';
|
||||
export declare class WebpackConfigBuilder {
|
||||
private config;
|
||||
private appConfig;
|
||||
private isDevelopmentMode;
|
||||
private isProductionMode;
|
||||
private cwd;
|
||||
private srcPath;
|
||||
constructor(appConfig: IWebpackAppConfig, mode: Mode);
|
||||
get configuration(): Configuration;
|
||||
applyOutput(): void;
|
||||
applyResolve(): void;
|
||||
applyDevServer(): void;
|
||||
applyPerfomance(): void;
|
||||
applyOptimizationRules(): void;
|
||||
applyModuleOptions(): void;
|
||||
applyPlugins(): void;
|
||||
merge(newConfig: Configuration): void;
|
||||
}
|
||||
//# sourceMappingURL=WebpackConfigBuilder.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"WebpackConfigBuilder.d.ts","sourceRoot":"","sources":["../../../src/config/webpack/WebpackConfigBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAQ7C,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,iBAAiB,CAAU;IACnC,OAAO,CAAC,gBAAgB,CAAU;IAClC,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,OAAO,CAAS;gBAEZ,SAAS,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI;IAmBpD,IAAW,aAAa,kBAEvB;IAEM,WAAW;IAYX,YAAY;IAmBZ,cAAc;IAWd,eAAe;IAUf,sBAAsB;IAMtB,kBAAkB;IAOlB,YAAY;IAmBZ,KAAK,CAAC,SAAS,EAAE,aAAa;CAGtC"}
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { IWebpackAppConfig, Mode } from '../../types';
|
||||
import type { Configuration } from 'webpack';
|
||||
/**
|
||||
* Создание конфига для сборки.
|
||||
*/
|
||||
export declare const createWebpackConfig: (environmentConfig: IWebpackAppConfig, mode?: Mode) => Configuration;
|
||||
export default createWebpackConfig;
|
||||
//# sourceMappingURL=createWebpackConfig.d.ts.map
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user