Skip to content

Commit 46b8b3a

Browse files
committed
fix: remove replace inject plugin with built-in copy; remove uglify
BREAKING CHANGE: requires webpack >=5 fixes #58
1 parent c87e845 commit 46b8b3a

File tree

10 files changed

+497
-177
lines changed

10 files changed

+497
-177
lines changed

.nycrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"functions": 90,
88
"statements": 90,
99
"exclude": [
10-
"test/**"
10+
"test/**",
11+
"src/app/utils/resolveDirectory.ts"
1112
]
1213
}

package-lock.json

Lines changed: 67 additions & 159 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"coverage": "nyc npm run test",
1616
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
1717
"format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\"",
18-
"lint": "tslint",
18+
"lint": "tslint -p ./src/app && tslint -p ./src/server",
1919
"docs": "typedoc",
2020
"preversion": "npm run format:check && npm run lint && npm test",
2121
"prepublish": "npm run build",
@@ -43,7 +43,6 @@
4343
"@types/mocha": "^10.0.3",
4444
"@types/node": "^20.8.7",
4545
"@types/sinon": "^10.0.20",
46-
"@types/uglify-js": "^3.17.3",
4746
"chai": "^4.3.10",
4847
"chai-as-promised": "^7.1.1",
4948
"chai-snapshot-matcher": "^2.0.3",
@@ -64,18 +63,18 @@
6463
"typedoc": "~0.23.28",
6564
"typedoc-plugin-missing-exports": "^1.0.0",
6665
"typedoc-plugin-rename-defaults": "0.6.6",
67-
"typescript": "~5.0.4",
68-
"webpack": "^5.96.1",
69-
"webpack-inject-plugin": "^1.5.5"
66+
"typescript": "~5.0.4"
7067
},
7168
"dependencies": {
7269
"@types/url-join": "^4.0.2",
7370
"avsc": "^5.7.7",
7471
"axios": "^1.5.1",
7572
"memoizee": "^0.4.15",
76-
"uglify-js": "^3.17.4",
7773
"url-join": "^4.0.1"
7874
},
75+
"peerDependencies": {
76+
"webpack": "^5.0.0"
77+
},
7978
"overrides": {
8079
"conventional-changelog-conventionalcommits": ">= 8.0.0"
8180
},

src/server/WebpackPlugins.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
import UglifyJs from 'uglify-js';
2-
import InjectPlugin, { ENTRY_ORDER } from 'webpack-inject-plugin';
3-
41
import { BannerPlugin, WebpackPluginInstance } from 'webpack';
52
import resolveDirectory from '../app/utils/resolveDirectory';
63
import { publicPathTpl } from './constants';
74
import { FactoryConfig } from './types';
5+
import { ENTRY_ORDER, WebpackInjectPlugin } from './webpack-inject-plugin/plugin';
86

97
/* istanbul ignore file */
108

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

6664
plugins.client.push(
67-
new InjectPlugin(
65+
new WebpackInjectPlugin(
6866
() => {
69-
const minifiedCode = UglifyJs.minify(resolveDirectory.toString()).code;
70-
return `${minifiedCode}
71-
__webpack_public_path__ = resolveDirectory(__ilc_script_url__, ${conf.publicPathDetection?.rootDirectoryLevel});`;
67+
return `${resolveDirectory.toString()}\n__webpack_public_path__ = resolveDirectory(__ilc_script_url__, ${conf.publicPathDetection?.rootDirectoryLevel});`;
7268
},
7369
{ entryOrder: ENTRY_ORDER.First },
7470
),
7571
);
7672

7773
plugins.server.push(
78-
new InjectPlugin(
74+
new WebpackInjectPlugin(
7975
() => `
8076
const pp = \`${conf.publicPathDetection!.ssrPublicPath}\`;
8177
if (!pp) {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { type LoaderContext } from 'webpack';
2+
import { registry } from './plugin';
3+
4+
type LoaderOptions = {
5+
id: string;
6+
};
7+
export function injectLoader(this: LoaderContext<LoaderOptions>, source: string | Buffer) {
8+
const options = this.getOptions();
9+
let func = (arg: any) => '';
10+
if (registry[options.id]) {
11+
func = registry[options.id];
12+
}
13+
14+
const rtn: any = func.call(this, source);
15+
16+
if (rtn instanceof Promise) {
17+
const callback = this.async();
18+
rtn.then((result) => {
19+
callback && callback(null, result);
20+
}).catch((err) => {
21+
callback && callback(err, undefined);
22+
});
23+
return undefined;
24+
}
25+
26+
return rtn;
27+
}
28+
29+
export default injectLoader;
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import path from 'path';
2+
import { Compiler, Entry, WebpackPluginInstance } from 'webpack';
3+
4+
type EntryType = string | string[] | Entry | (() => Promise<EntryType>) | any;
5+
type EntryFilterFunction = (entryName: string) => boolean;
6+
type EntryFilterType = string | EntryFilterFunction;
7+
8+
export type Loader = () => string;
9+
10+
export const registry: {
11+
[key: string]: Loader;
12+
} = {};
13+
14+
let uniqueIDCounter = 0;
15+
function getUniqueID() {
16+
const id = (++uniqueIDCounter).toString(16);
17+
18+
return `webpack-inject-module-${id}`;
19+
}
20+
21+
export enum ENTRY_ORDER {
22+
First = 1,
23+
Last,
24+
NotLast,
25+
}
26+
27+
export interface IInjectOptions {
28+
entryName?: EntryFilterType;
29+
entryOrder?: ENTRY_ORDER;
30+
loaderID?: string;
31+
}
32+
33+
function injectToArray(originalEntry: string[], newEntry: string, entryOrder = ENTRY_ORDER.NotLast): string[] {
34+
if (entryOrder === ENTRY_ORDER.First) {
35+
return [newEntry, ...originalEntry];
36+
}
37+
38+
if (entryOrder === ENTRY_ORDER.Last) {
39+
return [...originalEntry, newEntry];
40+
}
41+
42+
return [
43+
...originalEntry.splice(0, originalEntry.length - 1),
44+
newEntry,
45+
...originalEntry.splice(originalEntry.length - 1),
46+
];
47+
}
48+
49+
function createEntryFilter(filterOption?: EntryFilterType): EntryFilterFunction {
50+
if (filterOption === null || filterOption === undefined) {
51+
return () => true;
52+
}
53+
54+
if (typeof filterOption === 'string') {
55+
return (entryName: string) => filterOption === entryName;
56+
}
57+
58+
if (typeof filterOption === 'function') {
59+
return filterOption;
60+
}
61+
62+
throw new Error(`Unknown entry filter: ${typeof filterOption}`);
63+
}
64+
65+
export function injectEntry(
66+
originalEntry: EntryType | undefined,
67+
newEntry: string,
68+
options: IInjectOptions,
69+
): EntryType {
70+
if (originalEntry === undefined) {
71+
return newEntry;
72+
}
73+
74+
const filterFunc = createEntryFilter(options.entryName);
75+
76+
// Last module in an array gets exported, so the injected one must not be
77+
// last. https://webpack.github.io/docs/configuration.html#entry
78+
79+
if (typeof originalEntry === 'string') {
80+
return injectToArray([originalEntry], newEntry, options.entryOrder);
81+
}
82+
83+
if (Array.isArray(originalEntry)) {
84+
return injectToArray(originalEntry, newEntry, options.entryOrder);
85+
}
86+
87+
if (typeof originalEntry === 'function') {
88+
// The entry function is meant to be called on each compilation (when using --watch, webpack-dev-server)
89+
// We wrap the original function in our own function to reflect this behavior.
90+
return async () => {
91+
const callbackOriginEntry = await originalEntry();
92+
93+
// Safe type-cast here because callbackOriginEntry cannot be an EntryFunc,
94+
// so the injectEntry call won't return one either.
95+
return injectEntry(callbackOriginEntry, newEntry, options);
96+
};
97+
}
98+
if (Object.prototype.toString.call(originalEntry).slice(8, -1) === 'Object') {
99+
return Object.entries(originalEntry).reduce((a: Record<string, EntryType>, [key, entry]) => {
100+
if (filterFunc(key) || key === 'import') {
101+
a[key] = injectEntry(entry, newEntry, options);
102+
} else {
103+
a[key] = entry;
104+
}
105+
106+
return a;
107+
}, {});
108+
}
109+
110+
return originalEntry;
111+
}
112+
113+
export class WebpackInjectPlugin implements WebpackPluginInstance {
114+
private readonly options: IInjectOptions;
115+
116+
private readonly loader: Loader;
117+
118+
constructor(loader: Loader, options?: IInjectOptions) {
119+
this.loader = loader;
120+
this.options = {
121+
entryName: (options && options.entryName) || undefined,
122+
entryOrder: (options && options.entryOrder) || ENTRY_ORDER.NotLast,
123+
loaderID: (options && options.loaderID) || getUniqueID(),
124+
};
125+
}
126+
127+
apply(compiler: Compiler) {
128+
const id = this.options.loaderID!;
129+
const newEntry = path.resolve(__dirname, `loader?id=${id}!`);
130+
registry[id] = this.loader;
131+
compiler.options.entry = injectEntry(compiler.options.entry, newEntry, this.options);
132+
compiler.options.resolveLoader.extensions = ['.ts', '.js'];
133+
}
134+
}

0 commit comments

Comments
 (0)