Skip to content

Commit 4970a71

Browse files
committed
Add webpack-preserve-dynamic-require-plugin
1 parent 2547702 commit 4970a71

File tree

13 files changed

+281
-0
lines changed

13 files changed

+281
-0
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
## API Report File for "@rushstack/webpack-preserve-dynamic-require-plugin"
2+
3+
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
4+
5+
```ts
6+
7+
import type * as webpack from 'webpack';
8+
9+
// @public (undocumented)
10+
export class PreserveDynamicRequireWebpackPlugin {
11+
// (undocumented)
12+
apply(compiler: webpack.Compiler): void;
13+
}
14+
15+
// (No @packageDocumentation comment for this package)
16+
17+
```

rush.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,12 @@
10271027
"reviewCategory": "libraries",
10281028
"shouldPublish": true
10291029
},
1030+
{
1031+
"packageName": "@rushstack/webpack-preserve-dynamic-require-plugin",
1032+
"projectFolder": "webpack/preserve-dynamic-require-plugin",
1033+
"reviewCategory": "libraries",
1034+
"shouldPublish": true
1035+
},
10301036
{
10311037
"packageName": "@rushstack/set-webpack-public-path-plugin",
10321038
"projectFolder": "webpack/set-webpack-public-path-plugin",
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// This is a workaround for https://github.com/eslint/eslint/issues/3458
2+
require('@rushstack/eslint-config/patch/modern-module-resolution');
3+
4+
module.exports = {
5+
extends: [
6+
'@rushstack/eslint-config/profile/node-trusted-tool',
7+
'@rushstack/eslint-config/mixins/friendly-locals'
8+
],
9+
parserOptions: { tsconfigRootDir: __dirname }
10+
};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO.
2+
3+
# Ignore all files by default, to avoid accidentally publishing unintended files.
4+
*
5+
6+
# Use negative patterns to bring back the specific things we want to publish.
7+
!/bin/**
8+
!/lib/**
9+
!/lib-*/**
10+
!/dist/**
11+
!ThirdPartyNotice.txt
12+
13+
# Ignore certain patterns that should not get published.
14+
/dist/*.stats.*
15+
/lib/**/test/
16+
/lib-*/**/test/
17+
*.test.js
18+
19+
# NOTE: These don't need to be specified, because NPM includes them automatically.
20+
#
21+
# package.json
22+
# README (and its variants)
23+
# CHANGELOG (and its variants)
24+
# LICENSE / LICENCE
25+
26+
#--------------------------------------------
27+
# DO NOT MODIFY THE TEMPLATE ABOVE THIS LINE
28+
#--------------------------------------------
29+
30+
# (Add your project-specific overrides here)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# @rushstack/webpack-preserve-dynamic-require-plugin
2+
3+
## Overview
4+
5+
This Webpack plugin instructs webpack to leave dynamic usage of `require` as-is in the bundled code. For example, if your code contains:
6+
```js
7+
function requireSomeUserThing(path) {
8+
return require(path);
9+
}
10+
```
11+
The emitted bundle will preserve the call to `require(path)` instead of trying to process it.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
3+
4+
"mainEntryPointFilePath": "<projectFolder>/lib/index.d.ts",
5+
6+
"apiReport": {
7+
"enabled": true,
8+
"reportFolder": "../../../common/reviews/api"
9+
},
10+
11+
"docModel": {
12+
"enabled": true,
13+
"apiJsonFilePath": "../../../common/temp/api/<unscopedPackageName>.api.json"
14+
},
15+
16+
"dtsRollup": {
17+
"enabled": true
18+
}
19+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "@rushstack/heft-node-rig/profiles/default/config/jest.config.json"
3+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
// The "rig.json" file directs tools to look for their config files in an external package.
3+
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
4+
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
5+
6+
"rigPackageName": "@rushstack/heft-node-rig"
7+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "@rushstack/webpack-preserve-dynamic-require-plugin",
3+
"version": "0.8.11",
4+
"description": "This plugin tells webpack to leave dynamic calls to \"require\" as-is instead of trying to bundle them.",
5+
"main": "lib/index.js",
6+
"typings": "dist/webpack-preserve-dynamic-require-plugin.d.ts",
7+
"license": "MIT",
8+
"repository": {
9+
"type": "git",
10+
"url": "https://github.com/microsoft/rushstack.git",
11+
"directory": "webpack/preserve-dynamic-require-plugin"
12+
},
13+
"engines": {
14+
"node": ">=10.17.1"
15+
},
16+
"scripts": {
17+
"build": "heft build --clean",
18+
"_phase:build": "heft build --clean",
19+
"_phase:test": "heft test --no-build"
20+
},
21+
"dependencies": {},
22+
"devDependencies": {
23+
"@rushstack/eslint-config": "workspace:*",
24+
"@rushstack/heft": "workspace:*",
25+
"@rushstack/heft-node-rig": "workspace:*",
26+
"@types/heft-jest": "1.0.1",
27+
"@types/node": "12.20.24",
28+
"webpack": "~5.68.0"
29+
},
30+
"sideEffects": false
31+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`PreserveDynamicRequireWebpackPlugin Preserves dynamic require usage 1`] = `
4+
Map {
5+
"/release/main.js" => "/******/ (() => { // webpackBootstrap
6+
var __webpack_exports__ = {};
7+
require(process.env.SOME_PATH);
8+
/******/ })()
9+
;",
10+
}
11+
`;
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
jest.disableAutomock();
2+
import webpack from 'webpack';
3+
4+
import { PreserveDynamicRequireWebpackPlugin } from './index';
5+
6+
jest.setTimeout(1e9);
7+
8+
describe(PreserveDynamicRequireWebpackPlugin.name, () => {
9+
it('Preserves dynamic require usage', async () => {
10+
const sources: Map<string, string> = new Map();
11+
sources.set('/package.json', JSON.stringify({}));
12+
sources.set('/file.js', 'require(process.env.SOME_PATH);');
13+
14+
const compiler: webpack.Compiler = webpack({
15+
entry: {
16+
main: '/file.js'
17+
},
18+
output: {
19+
path: '/release',
20+
filename: '[name].js'
21+
},
22+
context: '/',
23+
mode: 'none',
24+
plugins: [new PreserveDynamicRequireWebpackPlugin()]
25+
});
26+
27+
const inputFs: typeof compiler.inputFileSystem = {
28+
readFile(path: string, cb: (err?: NodeJS.ErrnoException | undefined, content?: string) => void): void {
29+
cb(undefined, sources.get(path));
30+
},
31+
readdir(path: string, cb: (err?: NodeJS.ErrnoException | undefined, files?: string[]) => void): void {
32+
cb(
33+
undefined,
34+
Array.from(sources.keys(), (key: string) => key.slice(1))
35+
);
36+
},
37+
readlink(path: string, cb: (err?: NodeJS.ErrnoException | undefined, dest?: string) => void): void {
38+
cb();
39+
},
40+
stat(path: string, cb: (err?: NodeJS.ErrnoException, stat?: unknown) => void): void {
41+
if (sources.has(path)) {
42+
return cb(undefined, {
43+
isFile() {
44+
return true;
45+
},
46+
isDirectory() {
47+
return false;
48+
}
49+
});
50+
} else if (path === '/') {
51+
return cb(undefined, {
52+
isFile() {
53+
return false;
54+
},
55+
isDirectory() {
56+
return true;
57+
}
58+
});
59+
}
60+
cb(new Error(`Unexpected stat call for ${path}`));
61+
}
62+
} as unknown as typeof compiler.inputFileSystem;
63+
64+
const results: Map<string, string> = new Map();
65+
66+
const outputFs: typeof compiler.outputFileSystem = {
67+
mkdir(path: string, cb: (err?: NodeJS.ErrnoException) => void): void {
68+
cb();
69+
},
70+
stat(path: string, cb: (err?: NodeJS.ErrnoException) => void): void {
71+
const err: NodeJS.ErrnoException = new Error(`No such file`);
72+
err.code = 'ENOENT';
73+
cb(err);
74+
},
75+
writeFile(path: string, content: Buffer, cb: (err?: NodeJS.ErrnoException) => void): void {
76+
results.set(path, content.toString('utf8'));
77+
cb();
78+
}
79+
} as unknown as typeof compiler.outputFileSystem;
80+
81+
compiler.inputFileSystem = inputFs;
82+
compiler.outputFileSystem = outputFs;
83+
84+
await new Promise<void>((resolve: () => void, reject: (err: Error) => void) =>
85+
compiler.run((err?: Error | null) => {
86+
if (err) {
87+
return reject(err);
88+
}
89+
resolve();
90+
})
91+
);
92+
93+
expect(results).toMatchSnapshot();
94+
});
95+
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type * as webpack from 'webpack';
2+
3+
const PLUGIN_NAME: 'PreserveDynamicRequireWebpackPlugin' = 'PreserveDynamicRequireWebpackPlugin';
4+
5+
/**
6+
* @public
7+
*/
8+
export class PreserveDynamicRequireWebpackPlugin {
9+
public apply(compiler: webpack.Compiler): void {
10+
compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation: webpack.Compilation) => {
11+
function processDependencies(
12+
block: Pick<webpack.AsyncDependenciesBlock, 'dependencies' | 'blocks'>
13+
): void {
14+
const { dependencies } = block;
15+
for (let i: number = dependencies.length - 1; i >= 0; i--) {
16+
const dep: webpack.Dependency = dependencies[i];
17+
// Disable processing of dynamic require
18+
if (dep.constructor.name === 'CommonJsRequireContextDependency') {
19+
dependencies.splice(i, 1);
20+
}
21+
}
22+
23+
for (const child of block.blocks) {
24+
processDependencies(child);
25+
}
26+
}
27+
28+
compilation.hooks.succeedModule.tap(PLUGIN_NAME, (mod: webpack.Module) => {
29+
processDependencies(mod);
30+
});
31+
});
32+
}
33+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "./node_modules/@rushstack/heft-node-rig/profiles/default/tsconfig-base.json",
3+
"compilerOptions": {
4+
"target": "ES2019",
5+
"types": ["heft-jest", "node"],
6+
"noImplicitAny": false // Some typings are missing
7+
}
8+
}

0 commit comments

Comments
 (0)