Skip to content

Commit

Permalink
feat: rework and tuneup
Browse files Browse the repository at this point in the history
  • Loading branch information
seebeen committed Jun 15, 2024
1 parent c3551dd commit 377cdfe
Show file tree
Hide file tree
Showing 28 changed files with 1,378 additions and 749 deletions.
31 changes: 20 additions & 11 deletions lib/config/bundle.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { Expose, Transform, Type } from 'class-transformer';
import { Type } from 'class-transformer';
import {
IsBoolean,
IsHexColor,
IsInstance,
IsInt,
IsNotEmpty,
IsObject,
IsOptional,
IsPositive,
Expand All @@ -15,14 +14,14 @@ import {
import type { Configuration } from 'webpack';

export class BundleConfig {
@IsNotEmpty()
@IsString()
name!: string;

@IsString({ each: true })
files!: string[];

@IsBoolean()
@IsOptional()
splitChunks: boolean = false;

@IsOptional()
Expand All @@ -31,25 +30,35 @@ export class BundleConfig {
@ValidateIf((o: BundleConfig) => o.splitChunks === true)
chunkTest: RegExp = /[\\/]node_modules[\\/]/;

@IsString()
@ValidateIf((o: BundleConfig) => o.splitChunks === true)
@Transform(
({ value, obj }) => (value || 'vendor-[name]').replace('[name]', obj.name),
{ toClassOnly: true },
)
@Expose()
chunkName?: string;
@IsOptional()
chunkId: string = 'vendor-[name]';

@Min(20000)
@IsPositive()
@IsInt()
@ValidateIf((o: BundleConfig) => o.splitChunks === true)
@IsOptional()
chunkMinSize?: number;
@ValidateIf((o: BundleConfig) => o.splitChunks === true)
chunkMinSize: number = 20000;

@IsHexColor()
@IsOptional()
color?: string;

@IsObject()
@IsOptional()
override: Partial<Configuration> = {};

hasStyles(): boolean {
return this.files.some((f) => f.match(/\.s?css$/i));
}

hasScripts(): boolean {
return this.files.some((f) => f.match(/(\.[tj]sx?)$/i));
}

get chunkName(): string {
return this.chunkId.replace('[name]', this.name);
}
}
12 changes: 12 additions & 0 deletions lib/config/wordpack-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,16 @@ export class WordPackEnv {
@IsBoolean()
@IsOptional()
WEBPACK_WATCH?: boolean;

get base(): string {
return this.basePath;
}

get prod(): boolean {
return this.production;
}

resolve(...paths: string[]): string {
return path.posix.resolve(this.base, ...paths);
}
}
49 changes: 43 additions & 6 deletions lib/config/wordpack.config.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,37 @@
import { BundleConfig } from './bundle.config';
import { Expose, Type } from 'class-transformer';
import { Configuration } from 'webpack';
import { Expose, Transform, Type } from 'class-transformer';
import {
IsBoolean,
IsEnum,
IsNotEmpty,
IsObject,
IsOptional,
IsString,
ValidateIf,
ValidateNested,
} from 'class-validator';
import { Configuration } from 'webpack';
import { WordPackEnv } from './wordpack-env';
import { BundleConfig } from './bundle.config';

export class WordPackConfig extends WordPackEnv {
@IsString()
@Transform(({ value }) =>
(value || '[name].[contenthash:6][ext]').replace('[ext]', ''),
)
@IsOptional()
@Expose()
filename: string;

@IsString()
@IsOptional()
manifest: string = 'assets.json';

export class WordPackConfig {
@ValidateNested({ each: true })
@Type(() => BundleConfig)
bundles!: BundleConfig[];

@IsObject()
@IsOptional()
@IsOptional()
externals: Record<string, string> = {
jquery: 'jQuery',
underscore: '_',
Expand All @@ -27,30 +40,37 @@ export class WordPackConfig {

@IsBoolean()
@IsOptional()
@IsOptional()
multimode: boolean = true;

@IsString()
@IsNotEmpty()
@IsOptional()
srcBase?: string = 'assets';

@IsString()
@IsNotEmpty()
@IsOptional()
distBase?: string = 'dist';

@IsString()
@IsNotEmpty()
@IsOptional()
imageDir?: string = 'images';

@IsString()
@IsNotEmpty()
@IsOptional()
fontDir?: string = 'fonts';

@IsString()
@IsNotEmpty()
@IsOptional()
jsDir?: string = 'scripts';

@IsString()
@IsNotEmpty()
@IsOptional()
cssDir?: string = 'styles';

@IsEnum([
Expand All @@ -70,9 +90,10 @@ export class WordPackConfig {
globalChunks: string[] = ['awesome-notifications'];

@IsObject()
@IsOptional()
override: Partial<Configuration> = {};

get assetRoot(): string {
get srcDir(): string {
return this.srcBase as string;
}

Expand All @@ -95,4 +116,20 @@ export class WordPackConfig {
get styles(): string {
return this.cssDir as string;
}

get mode(): 'production' | 'development' {
return this.prod ? 'production' : 'development';
}

get asset(): string {
return this.prod ? this.filename : '[name]';
}

get isCI(): boolean {
return process.env.CI !== undefined;
}

get distPath(): string {
return this.resolve(this.distRoot);
}
}
37 changes: 33 additions & 4 deletions lib/functions/build-config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,38 @@
import { Configuration } from 'webpack';
import { WordPack } from '../services';
import { merge } from 'webpack-merge';
import * as fs from 'fs-extra';
import * as Svc from '../services';
import { WordPackEnv } from '../config';

export async function buildConfig(
env: Record<string, string>,
filepath?: string,
webpackEnv: Record<string, string> | WordPackEnv,
wpwpConfig: string = 'wpwp.config.ts',
): Promise<Configuration[]> {
return await new WordPack(env).buildConfig(filepath);
try {
const res: Configuration[] = [];
const env = Svc.UserConfig.env(webpackEnv);
const cfg = await Svc.UserConfig.load(wpwpConfig, env);

cfg.bundles.forEach((bundle) =>
res.push(
merge(
Svc.SharedConfig.build(cfg),
Svc.ManifestConfig.build(cfg),
Svc.EntryConfig.build(cfg, bundle),
Svc.CompileConfig.build(cfg, bundle),
Svc.OptimizeConfig.build(cfg, bundle),
cfg.override,
bundle.override,
),
),
);

res.push(Svc.AssetConfig.build(cfg));

fs.emptyDirSync(cfg.distPath);

return res;
} catch (e) {
return Promise.reject(e);
}
}
49 changes: 49 additions & 0 deletions lib/functions/manifest-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import WebpackAssetsManifest, { Entry } from 'webpack-assets-manifest';
import * as path from 'node:path';
import * as fs from 'fs-extra';

export function manifestEntryFormatter({ key, value }: Entry): Entry | false {
const base = (p: string) => path.basename(path.dirname(p));

const src = base(key);
const dst = base(value);

if (src !== dst) {
key = `${dst}/${key}`;
}

return { key, value };
}

export function manifestFileWriter(mfs: WebpackAssetsManifest): void {
const tmpl = `
<?php
/**
* Asset manifest file.
*
* Auto-generated via WordPack.
*
* @package eXtended WordPress
* @subpackage Assets
*/
return array(
{{{entries}}}
);
`;
const assets = Object.keys(mfs.assets);
const length = findLongest(assets);
const entries = assets.map((k) => parseEntry(k, mfs.assets[k], length));

const content = tmpl.replace('{{{entries}}}', entries.join('\n'));

fs.writeFileSync(mfs.getOutputPath().replace('.json', '.php'), content);
}

function findLongest(entries: string[]): number {
return entries.reduce((l, k) => Math.max(l, k.length), 0);
}

function parseEntry(key: string, val: unknown, length: number): string {
return ` '${key}'${' '.repeat(length - key.length)} => '${val}',`;
}
4 changes: 3 additions & 1 deletion lib/interfaces/wordpack-config.interface.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { BundleConfigInterface } from './bundle-config.interface';

export interface WordPackConfigInterface {
manifest?: string;
filename?: string;
bundles: BundleConfigInterface[];
multimode?: boolean;
externals?: Record<string, string>;
Expand All @@ -10,5 +12,5 @@ export interface WordPackConfigInterface {
fontDir?: string;
jsDir?: string;
cssDir?: string;
sourceMaps?: boolean;
sourceMaps?: string | false;
}
79 changes: 79 additions & 0 deletions lib/services/asset-config.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Configuration } from 'webpack';
import { WordPackConfig } from '../config';
import * as fs from 'fs-extra';
import CopyPlugin from 'copy-webpack-plugin';
import merge from 'webpack-merge';
import { OptimizeConfig } from './optimize-config.service';
import { ManifestConfig } from './manifest-config.service';
import { manifestFileWriter } from '../functions/manifest-utils';
import WebpackBarPlugin from 'webpackbar';

export class AssetConfig {
static build(cfg: WordPackConfig): Partial<Configuration> {
return merge(
AssetConfig.getCoreConfig(cfg),
AssetConfig.getManifestConfig(cfg),
AssetConfig.getOptimizeConfig(cfg),
);
}

private static getCoreConfig(cfg: WordPackConfig): Configuration {
return {
name: 'AssetCopy',
context: cfg.resolve(cfg.srcDir),
mode: cfg.mode,
entry: {},
output: {
path: cfg.resolve(cfg.distRoot),
},
stats: false,
plugins: [
new CopyPlugin({
patterns: [
{
from: `${cfg.images}/**/*`,
to: `[path]${cfg.asset}[ext]`,
force: false,
noErrorOnMissing: true,
filter: (rp) =>
!fs.existsSync(rp.replace(cfg.srcDir, cfg.distRoot)),
},
],
}),
new WebpackBarPlugin({
fancy: !cfg.isCI,
basic: cfg.isCI,
name: 'AssetCopy',
}),
],
dependencies: cfg.bundles.map(({ name }) => name),
};
}

private static getOptimizeConfig(cfg: WordPackConfig): Configuration {
if (!cfg.prod) {
return {};
}

return {
optimization: {
minimize: true,
minimizer: OptimizeConfig.getImageMinimizers(),
},
};
}
private static getManifestConfig(cfg: WordPackConfig): Configuration {
if (!cfg.prod || !cfg.manifest) {
return {};
}
return {
plugins: [
ManifestConfig.getManifestPlugin({
output: cfg.manifest,
writeToDisk: true,
done: manifestFileWriter,
}),
],
};
}
}
Loading

0 comments on commit 377cdfe

Please sign in to comment.