UNPKG

restrict-imports-loader

Version:

A Webpack loader to restrict imports in ES and TypeScript

281 lines (209 loc) 7.29 kB
# 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_](#limitations). _See also [the complete example repo](https://github.com/SimonAlling/restrict-imports-loader-example)._ Configuration example (`webpack.config.js`): ```javascript module.exports = { // ... module: { rules: [ { test: /\.tsx?$/, include: path.resolve(__dirname, "src"), use: [ { loader: "ts-loader", // or babel-loader, etc }, { loader: "restrict-imports-loader", options: { severity: "error", rules: [ { restricted: /^lodash$/, }, ], }, }, ], }, ], }, } ``` Source code (e.g. `src/index.ts`): ```typescript import * as ts from "typescript"; // OK import * as _ from "lodash"; // error import * as fp from "lodash/fp"; // OK (see "Restricting an entire package" for more info) ``` 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: ```javascript { loader: "restrict-imports-loader", options: { severity: "error", rules: [ { restricted: /^lodash$/, // inherits severity: "error" info: "Please import submodules instead of the full lodash package.", }, { restricted: /^typescript$/, severity: "warning", // no info specified; default is "Found restricted imports:" }, ], }, }, ``` ##### Using a function as decider If you provide a function as the `restricted` value, it must have the type ```typescript (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](https://webpack.js.org/configuration/entry-context/#context), or, if not specified, the "current working directory" as seen from Webpack's perspective.) ```typescript 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: ```javascript const { everythingInPackage } = require("restrict-imports-loader"); module.exports = { // ... { loader: "restrict-imports-loader", options: { severity: "error", rules: [ { restricted: everythingInPackage("lodash"), }, ], }, }, }; ``` Code: ```typescript import * as ts from "typescript"; // OK import * as ld from "lodasher"; // OK import * as _ from "lodash"; // error import * as fp from "lodash/fp"; // error ``` **Note:** `everythingInPackage` is `RegExp`-based, so it can't prevent the programmer from importing the restricted package using a relative import: ```typescript import * as _ from "../node_modules/lodash"; // OK ``` You must [use a function as decider](#using-a-function-as-decider) if you want to prevent that. See [_Blacklisting or whitelisting directories_](#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: ```typescript 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.) ```typescript 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");`](https://github.com/microsoft/TypeScript/blob/7cf6c70d90b60e962db417d80290288eb786b5fd/doc/spec.md#1133-import-require-declarations) _(works only in TypeScript)_ Dynamic imports are **not supported**: * `const ts = require("typescript");` * `const ts = import("typescript");` ## Contribute ```bash npm install npm run verify # build, lint and test ```