Skip to content

Commit

Permalink
fix: remove replace inject plugin with built-in copy; remove uglify
Browse files Browse the repository at this point in the history
BREAKING CHANGE: requires webpack >=5
fixes #58
  • Loading branch information
stas-nc committed Dec 30, 2024
1 parent c87e845 commit 46b8b3a
Show file tree
Hide file tree
Showing 10 changed files with 497 additions and 177 deletions.
3 changes: 2 additions & 1 deletion .nycrc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"functions": 90,
"statements": 90,
"exclude": [
"test/**"
"test/**",
"src/app/utils/resolveDirectory.ts"
]
}
226 changes: 67 additions & 159 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 5 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"coverage": "nyc npm run test",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\"",
"lint": "tslint",
"lint": "tslint -p ./src/app && tslint -p ./src/server",
"docs": "typedoc",
"preversion": "npm run format:check && npm run lint && npm test",
"prepublish": "npm run build",
Expand Down Expand Up @@ -43,7 +43,6 @@
"@types/mocha": "^10.0.3",
"@types/node": "^20.8.7",
"@types/sinon": "^10.0.20",
"@types/uglify-js": "^3.17.3",
"chai": "^4.3.10",
"chai-as-promised": "^7.1.1",
"chai-snapshot-matcher": "^2.0.3",
Expand All @@ -64,18 +63,18 @@
"typedoc": "~0.23.28",
"typedoc-plugin-missing-exports": "^1.0.0",
"typedoc-plugin-rename-defaults": "0.6.6",
"typescript": "~5.0.4",
"webpack": "^5.96.1",
"webpack-inject-plugin": "^1.5.5"
"typescript": "~5.0.4"
},
"dependencies": {
"@types/url-join": "^4.0.2",
"avsc": "^5.7.7",
"axios": "^1.5.1",
"memoizee": "^0.4.15",
"uglify-js": "^3.17.4",
"url-join": "^4.0.1"
},
"peerDependencies": {
"webpack": "^5.0.0"
},
"overrides": {
"conventional-changelog-conventionalcommits": ">= 8.0.0"
},
Expand Down
12 changes: 4 additions & 8 deletions src/server/WebpackPlugins.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import UglifyJs from 'uglify-js';
import InjectPlugin, { ENTRY_ORDER } from 'webpack-inject-plugin';

import { BannerPlugin, WebpackPluginInstance } from 'webpack';
import resolveDirectory from '../app/utils/resolveDirectory';
import { publicPathTpl } from './constants';
import { FactoryConfig } from './types';
import { ENTRY_ORDER, WebpackInjectPlugin } from './webpack-inject-plugin/plugin';

/* istanbul ignore file */

Expand Down Expand Up @@ -64,18 +62,16 @@ export function WebpackPluginsFactory(config: RegExp | FactoryConfig = {}): Fact
}

plugins.client.push(
new InjectPlugin(
new WebpackInjectPlugin(
() => {
const minifiedCode = UglifyJs.minify(resolveDirectory.toString()).code;
return `${minifiedCode}
__webpack_public_path__ = resolveDirectory(__ilc_script_url__, ${conf.publicPathDetection?.rootDirectoryLevel});`;
return `${resolveDirectory.toString()}\n__webpack_public_path__ = resolveDirectory(__ilc_script_url__, ${conf.publicPathDetection?.rootDirectoryLevel});`;
},
{ entryOrder: ENTRY_ORDER.First },
),
);

plugins.server.push(
new InjectPlugin(
new WebpackInjectPlugin(
() => `
const pp = \`${conf.publicPathDetection!.ssrPublicPath}\`;
if (!pp) {
Expand Down
29 changes: 29 additions & 0 deletions src/server/webpack-inject-plugin/loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { type LoaderContext } from 'webpack';
import { registry } from './plugin';

type LoaderOptions = {
id: string;
};
export function injectLoader(this: LoaderContext<LoaderOptions>, source: string | Buffer) {
const options = this.getOptions();
let func = (arg: any) => '';
if (registry[options.id]) {
func = registry[options.id];
}

const rtn: any = func.call(this, source);

if (rtn instanceof Promise) {
const callback = this.async();
rtn.then((result) => {
callback && callback(null, result);
}).catch((err) => {
callback && callback(err, undefined);
});
return undefined;
}

return rtn;
}

export default injectLoader;
134 changes: 134 additions & 0 deletions src/server/webpack-inject-plugin/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import path from 'path';
import { Compiler, Entry, WebpackPluginInstance } from 'webpack';

type EntryType = string | string[] | Entry | (() => Promise<EntryType>) | any;
type EntryFilterFunction = (entryName: string) => boolean;
type EntryFilterType = string | EntryFilterFunction;

export type Loader = () => string;

export const registry: {
[key: string]: Loader;
} = {};

let uniqueIDCounter = 0;
function getUniqueID() {
const id = (++uniqueIDCounter).toString(16);

return `webpack-inject-module-${id}`;
}

export enum ENTRY_ORDER {
First = 1,
Last,
NotLast,
}

export interface IInjectOptions {
entryName?: EntryFilterType;
entryOrder?: ENTRY_ORDER;
loaderID?: string;
}

function injectToArray(originalEntry: string[], newEntry: string, entryOrder = ENTRY_ORDER.NotLast): string[] {
if (entryOrder === ENTRY_ORDER.First) {
return [newEntry, ...originalEntry];
}

if (entryOrder === ENTRY_ORDER.Last) {
return [...originalEntry, newEntry];
}

return [
...originalEntry.splice(0, originalEntry.length - 1),
newEntry,
...originalEntry.splice(originalEntry.length - 1),
];
}

function createEntryFilter(filterOption?: EntryFilterType): EntryFilterFunction {
if (filterOption === null || filterOption === undefined) {
return () => true;
}

if (typeof filterOption === 'string') {
return (entryName: string) => filterOption === entryName;
}

if (typeof filterOption === 'function') {
return filterOption;
}

throw new Error(`Unknown entry filter: ${typeof filterOption}`);
}

export function injectEntry(
originalEntry: EntryType | undefined,
newEntry: string,
options: IInjectOptions,
): EntryType {
if (originalEntry === undefined) {
return newEntry;
}

const filterFunc = createEntryFilter(options.entryName);

// Last module in an array gets exported, so the injected one must not be
// last. https://webpack.github.io/docs/configuration.html#entry

if (typeof originalEntry === 'string') {
return injectToArray([originalEntry], newEntry, options.entryOrder);
}

if (Array.isArray(originalEntry)) {
return injectToArray(originalEntry, newEntry, options.entryOrder);
}

if (typeof originalEntry === 'function') {
// The entry function is meant to be called on each compilation (when using --watch, webpack-dev-server)
// We wrap the original function in our own function to reflect this behavior.
return async () => {
const callbackOriginEntry = await originalEntry();

// Safe type-cast here because callbackOriginEntry cannot be an EntryFunc,
// so the injectEntry call won't return one either.
return injectEntry(callbackOriginEntry, newEntry, options);
};
}
if (Object.prototype.toString.call(originalEntry).slice(8, -1) === 'Object') {
return Object.entries(originalEntry).reduce((a: Record<string, EntryType>, [key, entry]) => {
if (filterFunc(key) || key === 'import') {
a[key] = injectEntry(entry, newEntry, options);
} else {
a[key] = entry;
}

return a;
}, {});
}

return originalEntry;
}

export class WebpackInjectPlugin implements WebpackPluginInstance {
private readonly options: IInjectOptions;

private readonly loader: Loader;

constructor(loader: Loader, options?: IInjectOptions) {
this.loader = loader;
this.options = {
entryName: (options && options.entryName) || undefined,
entryOrder: (options && options.entryOrder) || ENTRY_ORDER.NotLast,
loaderID: (options && options.loaderID) || getUniqueID(),
};
}

apply(compiler: Compiler) {
const id = this.options.loaderID!;
const newEntry = path.resolve(__dirname, `loader?id=${id}!`);
registry[id] = this.loader;
compiler.options.entry = injectEntry(compiler.options.entry, newEntry, this.options);
compiler.options.resolveLoader.extensions = ['.ts', '.js'];
}
}
Loading

0 comments on commit 46b8b3a

Please sign in to comment.