restrict-imports-loader
A Webpack loader to restrict imports in ES and TypeScript.
Installation
npm install --save-dev restrict-imports-loader
Usage
NOTE: Only static imports are supported; see Limitations.
See also the complete example repo.
Configuration example (webpack.config.js
):
module.exports = {
module: {
rules: [
{
test: /\.tsx?$/,
include: path.resolve(__dirname, "src"),
use: [
{
loader: "awesome-typescript-loader",
},
{
loader: "restrict-imports-loader",
options: {
severity: "error",
rules: [
{
restricted: /^lodash$/,
},
],
},
},
],
},
],
},
}
Source code (e.g. src/index.ts
):
import * as ts from "typescript";
import * as _ from "lodash";
import * as fp from "lodash/fp";
Webpack output:
ERROR in ./src/index.ts
Module build failed (from ../restrict-imports-loader/dist/index.js):
Found restricted imports:
• "lodash", imported on line 2:
import * as _ from "lodash";
Options
severity
You can control what happens if a restricted import is found by setting the severity
option to either "fatal"
(stop compilation), "error"
(emit error) or "warning"
(emit warning).
The severity level can be overridden for individual rules; see below.
rules
Must be a list in which each element has a restricted
property with a RegExp
or function value.
Each rule can also override the severity
defined for the loader.
Example:
{
loader: "restrict-imports-loader",
options: {
severity: "error",
rules: [
{
restricted: /^lodash$/,
info: "Please import submodules instead of the full lodash package.",
},
{
restricted: /^typescript$/,
severity: "warning",
},
],
},
},
Using a function as decider
If you provide a function as the restricted
value, it must have the type
(string, webpack.loader.LoaderContext) => Promise<boolean>
where the string
parameter represents the import path in each import statement, e.g. typescript
in import * as ts from "typescript";
.
This way, you can use any algorithm you want to determine if an import should be restricted, possibly depending on the loader context.
In the example below (written in TypeScript), if decider
is used as the restricted
value, all imports from outside the project root directory are restricted.
(The "project root directory" is whatever directory you've specified using Webpack's context
option, or, if not specified, the "current working directory" as seen from Webpack's perspective.)
import { LoaderDecider } from "restrict-imports-loader";
const decider: LoaderDecider = (importPath, loaderContext) => new Promise((resolve, reject) => {
loaderContext.resolve(loaderContext.context, importPath, (err, result) => {
if (err === null) {
resolve(false === result.startsWith(loaderContext.rootContext));
} else {
reject(err.message);
}
});
});
detailedErrorMessages
By default, error messages include the faulty import statements exactly as written, as well as any extra info provided by the decider, for example:
Found restricted imports:
• "typescript", imported on line 1:
import * as _ from "typescript";
(resolved: node_modules/typescript/lib/typescript.js)
Setting detailedErrorMessages
to false
means that error messages will only include the import path and line number:
Found restricted imports:
• "typescript", imported on line 1
Note that Webpack will always show the file name (e.g. ERROR in ./src/main.ts
).
Restricting an entire package
If you want to restrict an entire package, including its submodules, you can use everythingInPackage
for convenience and readability:
const { everythingInPackage } = require("restrict-imports-loader");
module.exports = {
{
loader: "restrict-imports-loader",
options: {
severity: "error",
rules: [
{
restricted: everythingInPackage("lodash"),
},
],
},
},
};
Code:
import * as ts from "typescript";
import * as ld from "lodasher";
import * as _ from "lodash";
import * as fp from "lodash/fp";
Note: everythingInPackage
is RegExp
-based, so it can't prevent the programmer from importing the restricted package using a relative import:
import * as _ from "../node_modules/lodash";
You must use a function as decider if you want to prevent that.
See Blacklisting or whitelisting directories for a convenient approach.
Blacklisting or whitelisting directories
You can use everythingInside
or everythingOutside
to blacklist or whitelist, respectively, a set of absolute directories:
const { everythingOutside } = require("restrict-imports-loader");
module.exports = {
{
loader: "restrict-imports-loader",
options: {
severity: "warning",
rules: [
{
restricted: everythingOutside([
path.resolve(__dirname, "node_modules"),
path.resolve(__dirname, "src"),
]),
info: `Imports should resolve to 'node_modules' or 'src'. These do not:`,
},
],
},
},
};
Restricting excessive directory climbing
You can restrict the number of consecutive ../
s an import may contain.
(.././../
counts as two consecutive ../
s.)
const { climbingUpwardsMoreThan } = require("restrict-imports-loader");
module.exports = {
{
loader: "restrict-imports-loader",
options: {
severity: "warning",
rules: [
{
restricted: climbingUpwardsMoreThan(1),
info: `These imports climb the directory tree excessively:`,
},
],
},
},
};
This example allows "../foo"
but restricts "../../foo"
.
Limitations
Only static ES2015 (ES6) imports are supported, for example:
import {} from "typescript";
import * as ts from "typescript";
import ts from "typescript";
import "typescript";
export {} from "typescript";
import ts = require("typescript");
(works only in TypeScript)
Dynamic imports are not supported:
const ts = require("typescript");
const ts = import("typescript");
Contribute
npm install
npm run verify