Files
vscode/.eslint-plugin-local/code-no-http-import.ts
Benjamin Pasero 855722ab9c Startup perf regression due to top level import of http (fix #294857) (#294894)
* Startup perf regression due to top level import of http (fix #294857)

* Startup perf regression due to top level import of `http` (fix #294857)

* .
2026-02-12 14:58:23 +01:00

79 lines
2.4 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TSESTree } from '@typescript-eslint/typescript-estree';
import * as eslint from 'eslint';
import { normalize } from 'path';
import minimatch from 'minimatch';
import { createImportRuleListener } from './utils.ts';
const restrictedModules = new Set(['http', 'https']);
const REPO_ROOT = normalize(`${import.meta.dirname}/..`);
export default new class implements eslint.Rule.RuleModule {
readonly meta: eslint.Rule.RuleMetaData = {
messages: {
notAllowed: 'Importing \'{{module}}\' is only allowed as a type import (`import type ...`) to prevent startup performance regressions as these modules are slow to load. Use dynamic `import(\'{{module}}\')` for runtime access.'
},
schema: {
type: 'array',
items: {
type: 'object',
properties: {
target: {
type: 'string',
description: 'A glob pattern for files to check'
}
},
additionalProperties: false,
required: ['target']
}
}
};
create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
const targets = (context.options as { target: string }[]).map(o => o.target);
if (targets.length > 0) {
const relativeFilename = normalize(context.getFilename()).substring(REPO_ROOT.length + 1).replace(/\\/g, '/');
const matched = targets.some(pattern => minimatch(relativeFilename, pattern));
if (!matched) {
return {}; // file is not covered by any target pattern
}
}
return createImportRuleListener((node, path) => {
if (!restrictedModules.has(path)) {
return;
}
const parent = node.parent;
if (!parent) {
return;
}
// Allow: import type { ... } from 'http'
// Allow: import type * as http from 'http'
if (parent.type === TSESTree.AST_NODE_TYPES.ImportDeclaration && parent.importKind === 'type') {
return;
}
// Allow: export type { ... } from 'http'
if ('exportKind' in parent && parent.exportKind === 'type') {
return;
}
context.report({
loc: parent.loc,
messageId: 'notAllowed',
data: {
module: path
}
});
});
}
};