Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

restrict-imports-loader

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

restrict-imports-loader - npm Package Compare versions

Comparing version 2.0.0 to 3.0.0

dist/deciders.d.ts

22

dist/core.d.ts
import ts from "typescript";
export declare type ImportDetails = Readonly<{
declare type ImportDetails = Readonly<{
path: string;
node: ts.Node;
line: number;
}>;
export declare type Decider = RegExp | ((importPath: string) => boolean);
export declare type Deciders = readonly Decider[];
export declare function check(x: {
export declare type RestrictedImportDetails = ImportDetails & Readonly<{
info: string | undefined;
}>;
export declare type Decision = {
restricted: false;
} | {
restricted: true;
info?: string;
};
export declare type AsyncDeciderFunction = (importPath: string) => Promise<Decision>;
export declare function checkAsync(x: {
source: string;
deciders: Deciders;
deciders: readonly AsyncDeciderFunction[];
fileName: string;
setParentNodes: boolean;
}): ReadonlyArray<ReadonlyArray<ImportDetails>>;
}): Promise<ReadonlyArray<ReadonlyArray<RestrictedImportDetails>>>;
export {};

@@ -7,32 +7,41 @@ "use strict";

const typescript_1 = __importDefault(require("typescript"));
function check(x) {
async function checkAsync(x) {
const sourceFile = typescript_1.default.createSourceFile(x.fileName, x.source, typescript_1.default.ScriptTarget.Latest, x.setParentNodes);
return x.deciders.map(decider => badImportsIn(sourceFile, decider));
const imports = importsIn(sourceFile);
return Promise.all(x.deciders.map(decider => onlyRestricted(decider, imports)));
}
exports.check = check;
function badImportsIn(rootNode, decider) {
const errorAccumulator = [];
exports.checkAsync = checkAsync;
async function onlyRestricted(decider, is) {
const isAndDecisions = await Promise.all(is.map(i => decider(i.path).then(decision => ({ i, decision }))));
const results = [];
for (const iAndD of isAndDecisions) {
if (iAndD.decision.restricted) {
results.push({ ...iAndD.i, info: iAndD.decision.info });
}
}
return results;
}
function importsIn(rootNode) {
const accumulator = [];
const getLineNumber = ((node) => 1 + rootNode.getLineAndCharacterOfPosition(node.pos).line);
typescript_1.default.forEachChild(rootNode, node => {
if (isInteresting(node))
checkInteresting(node, decider, errorAccumulator);
lookForImportsIn(node, accumulator, getLineNumber);
});
return errorAccumulator;
return accumulator;
}
function checkInteresting(interestingNode, decider, errorAccumulator) {
function lookForImportsIn(interestingNode, accumulator, getLineNumber) {
interestingNode.forEachChild(node => {
if (typescript_1.default.isStringLiteral(node) || typescript_1.default.isExternalModuleReference(node)) {
const importPath = (typescript_1.default.isExternalModuleReference(node)
const stringLiteral = (typescript_1.default.isExternalModuleReference(node)
? node.expression
: node).text;
if (isRestricted(importPath, decider)) {
errorAccumulator.push({ path: importPath, node: interestingNode });
}
: node);
accumulator.push({
path: stringLiteral.text,
node: interestingNode,
line: getLineNumber(stringLiteral),
});
}
});
}
function isRestricted(name, decider) {
return (decider instanceof RegExp
? decider.test(name)
: decider(name));
}
function isInteresting(node) {

@@ -39,0 +48,0 @@ return [

import * as webpack from "webpack";
import * as core from "./core";
export { Decider, Deciders, ImportDetails } from "./core";
export { LoaderOptions, Severity } from "./loader";
export default function (this: webpack.loader.LoaderContext, source: string): string;
export declare function check(x: {
source: string;
restricted: core.Deciders;
fileName?: string;
setParentNodes?: boolean;
}): ReadonlyArray<ReadonlyArray<core.ImportDetails>>;
export declare function everythingIn(packageName: string): RegExp;
export { RestrictedImportDetails, checkAsync } from "./core";
export { LoaderDecider, LoaderOptions, Severity } from "./loader";
export { everythingInPackage, everythingOutside, everythingInside, matchedBy } from "./deciders";
export default function (this: webpack.loader.LoaderContext, source: string): void;
export declare type AsyncDecider = RegExp | core.AsyncDeciderFunction;

@@ -10,22 +10,14 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("./core"));
const loader = __importStar(require("./loader"));
const utilities_1 = require("./utilities");
var core_1 = require("./core");
exports.checkAsync = core_1.checkAsync;
var deciders_1 = require("./deciders");
exports.everythingInPackage = deciders_1.everythingInPackage;
exports.everythingOutside = deciders_1.everythingOutside;
exports.everythingInside = deciders_1.everythingInside;
exports.matchedBy = deciders_1.matchedBy;
function default_1(source) {
return loader.run(this, source);
loader.run(this, source);
}
exports.default = default_1;
function check(x) {
return core.check({
source: x.source,
deciders: x.restricted,
fileName: utilities_1.defaultTo("", x.fileName),
setParentNodes: utilities_1.defaultTo(true, x.setParentNodes),
});
}
exports.check = check;
function everythingIn(packageName) {
return new RegExp(String.raw `^${packageName}(\/.*)?$`);
}
exports.everythingIn = everythingIn;
//# sourceMappingURL=index.js.map
import * as webpack from "webpack";
import * as core from "./core";
export declare type Severity = "fatal" | "error" | "warning";
declare type LoaderContext = webpack.loader.LoaderContext;
export declare type LoaderFunctionDecider = (importPath: string, loaderContext: webpack.loader.LoaderContext) => Promise<core.Decision>;
export declare type LoaderDecider = RegExp | LoaderFunctionDecider;
declare type LoaderRule = {
restricted: RegExp;
restricted: LoaderDecider;
severity?: Severity;

@@ -13,3 +17,3 @@ info?: string;

};
export declare function run(loaderContext: webpack.loader.LoaderContext, source: string): string;
export declare function run(loaderContext: LoaderContext, source: string): void;
export {};

@@ -16,2 +16,3 @@ "use strict";

const core = __importStar(require("./core"));
const deciders = __importStar(require("./deciders"));
const text_1 = require("./text");

@@ -42,4 +43,7 @@ const utilities_1 = require("./utilities");

restricted: {
description: `Regular expressions specifying which imports should be restricted.`,
instanceof: "RegExp",
description: `Regular expression or function (of type (string, webpack.loader.LoaderContext) => Promise<boolean>) specifying which imports should be restricted.`,
anyOf: [
{ instanceof: "RegExp" },
{ instanceof: "Function" },
],
},

@@ -69,36 +73,47 @@ severity: {

function run(loaderContext, source) {
const callback = loaderContext.async();
if (callback === undefined)
throw new Error(`Webpack did not provide an async callback.`);
const options = loader_utils_1.getOptions(loaderContext);
schema_utils_1.default(SCHEMA, options, CONFIG);
const rules = options.rules;
const setParentNodes = utilities_1.defaultTo(true, options.detailedErrorMessages);
const badImportMatrix = core.check({
const detailedErrorMessages = utilities_1.defaultTo(DEFAULT.detailedErrorMessages, options.detailedErrorMessages);
core.checkAsync({
source: source,
deciders: rules.map(r => r.restricted),
deciders: rules.map(r => r.restricted).map(deciderFunction(loaderContext)),
fileName: loaderContext.resourcePath,
setParentNodes: setParentNodes,
});
rules.forEach((rule, i) => {
const badImports = badImportMatrix[i];
if (badImports.length > 0) {
const severity = utilities_1.defaultTo(options.severity, rule.severity);
const info = utilities_1.defaultTo(DEFAULT.info, rule.info);
const err = new Error(errorMessageForAll(badImports, info, setParentNodes));
switch (severity) {
case "fatal":
throw err;
case "error":
loaderContext.emitError(err);
break;
case "warning":
loaderContext.emitWarning(err);
break;
default:
const _ = severity;
throw _;
setParentNodes: detailedErrorMessages,
}).then(badImportMatrix => {
rules.forEach((rule, i) => {
const badImports = badImportMatrix[i];
if (badImports.length > 0) {
const severity = utilities_1.defaultTo(options.severity, rule.severity);
const info = utilities_1.defaultTo(DEFAULT.info, rule.info);
const err = new Error(errorMessageForAll(badImports, info, detailedErrorMessages));
switch (severity) {
case "fatal":
throw err;
case "error":
loaderContext.emitError(err);
break;
case "warning":
loaderContext.emitWarning(err);
break;
default:
const _ = severity;
throw _;
}
}
}
});
callback(null, source);
}).catch(err => {
callback(err, source);
});
return source;
}
exports.run = run;
function deciderFunction(loaderContext) {
return decider => (decider instanceof RegExp
? deciders.matchedBy(decider)
: importPath => decider(importPath, loaderContext));
}
function errorMessageForAll(imports, info, setParentNodesWasUsed) {

@@ -116,11 +131,11 @@ return [

? [
`, imported here:`,
`:`,
``,
text_1.indentBy(6)(i.node.getText()),
``,
``,
i.info ? text_1.indentBy(2)(i.info) + "\n\n" : "",
].join("\n")
: "");
return i => `• ` + text_1.quote(i.path) + details(i) + `\n`;
return i => `• ` + text_1.quote(i.path) + `, imported on line ${i.line}` + details(i) + `\n`;
}
//# sourceMappingURL=loader.js.map
{
"name": "restrict-imports-loader",
"version": "2.0.0",
"version": "3.0.0",
"description": "A Webpack loader to restrict imports in ES and TypeScript",

@@ -44,5 +44,5 @@ "keywords": [

"rename": "renamer --force --find \"/\\.js$/\" --replace \".mjs\" \"dist/**\"",
"jest": "jest",
"test": "npm run lint && npm run jest",
"verify": "repository-check-dirty && npm run build && npm test && npm pack"
"test": "jest",
"test-clean": "jest --no-cache",
"verify": "repository-check-dirty && npm run build && npm run lint && npm run test-clean && npm pack"
},

@@ -66,2 +66,3 @@ "sideEffects": false,

"devDependencies": {
"@types/enhanced-resolve": "^3.0.6",
"@types/jest": "^24.0.23",

@@ -71,2 +72,3 @@ "@types/webpack": "^4.39.9",

"cli-confirm": "^1.0.1",
"enhanced-resolve": "^4.1.1",
"jest": "^24.9.0",

@@ -73,0 +75,0 @@ "no-emit-webpack-plugin": "^2.0.1",

@@ -13,4 +13,6 @@ # restrict-imports-loader

**NOTE:** Only static imports are supported; see _Limitations_.
**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`):

@@ -26,3 +28,3 @@

include: path.resolve(__dirname, "src"),
loaders: [
use: [
{

@@ -54,3 +56,3 @@ loader: "awesome-typescript-loader", // or babel-loader, etc

import * as _ from "lodash"; // error
import * as fp from "lodash/fp"; // OK (see "Restrict Entire Package" for more info)
import * as fp from "lodash/fp"; // OK (see "Restricting an entire package" for more info)
```

@@ -65,3 +67,3 @@

• "lodash", imported here:
• "lodash", imported on line 2:

@@ -82,3 +84,3 @@ import * as _ from "lodash";

Must be a list in which each element has a `restricted` property with a `RegExp` value.
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.

@@ -108,6 +110,34 @@ Example:

##### 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 statement exactly as written:
By default, error messages include the faulty import statements exactly as written, as well as any extra info provided by the decider, for example:

@@ -117,8 +147,10 @@ ```

• "typescript", imported here:
• "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:
Setting `detailedErrorMessages` to `false` means that error messages will only include the import path and line number:

@@ -128,3 +160,3 @@ ```

• "typescript"
• "typescript", imported on line 1
```

@@ -135,8 +167,8 @@

### Restrict Entire Package
### Restricting an entire package
If you want to restrict an entire package, including its submodules, you can use `everythingIn` for convenience and readability:
If you want to restrict an entire package, including its submodules, you can use `everythingInPackage` for convenience and readability:
```javascript
const { everythingIn } = require("restrict-imports-loader");
const { everythingInPackage } = require("restrict-imports-loader");

@@ -151,3 +183,3 @@ module.exports = {

{
restricted: everythingIn("lodash"),
restricted: everythingInPackage("lodash"),
},

@@ -164,2 +196,3 @@ ],

import * as ts from "typescript"; // OK
import * as ld from "lodasher"; // OK
import * as _ from "lodash"; // error

@@ -169,4 +202,40 @@ 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:`,
},
],
},
},
};
```
### Limitations

@@ -173,0 +242,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc