/
githubmirror
/
babel
Обзор
Документация
Войти
/
githubmirror
/
babel
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
packages/babel-helper-create-class-features-plugin/src/features.ts
184 строки
6 KB
liuxingbaoyu
Enable `strictNullChecks` for `create-class-features-plugin` (#17824)
24 фев 2026, 21:52
Не верифицирован
24 фев 2026, 21:52
6b9c30c
Код
Авторство
О чём код?
import type { File, types as t } from "@babel/core"; import type { NodePath } from "@babel/core"; import { hasOwnDecorators } from "./decorators.ts"; export const FEATURES = Object.freeze({ //classes: 1 << 0, fields: 1 << 1, privateMethods: 1 << 2, decorators: 1 << 3, privateIn: 1 << 4, staticBlocks: 1 << 5, }); const featuresSameLoose = new Map([ [FEATURES.fields, "@babel/plugin-transform-class-properties"], [FEATURES.privateMethods, "@babel/plugin-transform-private-methods"], [FEATURES.privateIn, "@babel/plugin-transform-private-property-in-object"], ]); // We can't use a symbol because this needs to always be the same, even if // this package isn't deduped by npm. e.g. // - node_modules/ // - @babel/plugin-class-features // - @babel/plugin-proposal-decorators // - node_modules // - @babel-plugin-class-features const featuresKey = "@babel/plugin-class-features/featuresKey"; const looseKey = "@babel/plugin-class-features/looseKey"; export function enableFeature(file: File, feature: number, loose: boolean) { // We can't blindly enable the feature because, if it was already set, // "loose" can't be changed, so that // @babel/plugin-class-properties { loose: true } // @babel/plugin-class-properties { loose: false } // is transformed in loose mode. // We only enabled the feature if it was previously disabled. if (!hasFeature(file, feature)) { file.set(featuresKey, file.get(featuresKey) | feature); setLoose(file, feature, loose); } let resolvedLoose: boolean | undefined; for (const [mask] of featuresSameLoose) { if (!hasFeature(file, mask)) continue; const loose = isLoose(file, mask); if (resolvedLoose === !loose) { throw new Error( "'loose' mode configuration must be the same for @babel/plugin-transform-class-properties, " + "@babel/plugin-transform-private-methods and " + "@babel/plugin-transform-private-property-in-object (when they are enabled)." + "\n\n" + getBabelShowConfigForHint(file), ); } else { resolvedLoose = loose; } } } function getBabelShowConfigForHint(file: File) { let { filename } = file.opts; if (!filename || filename === "unknown") { filename = "[name of the input file]"; } return `\ If you already set the same 'loose' mode for these plugins in your config, it's possible that they \ are enabled multiple times with different options. You can re-run Babel with the BABEL_SHOW_CONFIG_FOR environment variable to show the loaded \ configuration: \tnpx cross-env BABEL_SHOW_CONFIG_FOR=${filename} <your build command> See https://babeljs.io/docs/configuration#print-effective-configs for more info.`; } function hasFeature(file: File, feature: number) { return !!(file.get(featuresKey) & feature); } export function isLoose(file: File, feature: number) { return !!(file.get(looseKey) & feature); } function setLoose(file: File, feature: number, loose: boolean) { if (loose) file.set(looseKey, file.get(looseKey) | feature); else file.set(looseKey, file.get(looseKey) & ~feature); } export function shouldTransform(path: NodePath<t.Class>, file: File): boolean { let decoratorPath: NodePath<t.Decorator | null> | null = null; let publicFieldPath: NodePath<t.ClassProperty> | null = null; let privateFieldPath: NodePath<t.ClassPrivateProperty> | null = null; let privateMethodPath: NodePath<t.ClassPrivateMethod> | null = null; let staticBlockPath: NodePath<t.StaticBlock> | null = null; if (hasOwnDecorators(path.node)) { decoratorPath = path.get("decorators.0"); } for (const el of path.get("body.body")) { if (!decoratorPath && hasOwnDecorators(el.node)) { decoratorPath = el.get("decorators.0"); } if (!publicFieldPath && el.isClassProperty()) { publicFieldPath = el; } if (!privateFieldPath && el.isClassPrivateProperty()) { privateFieldPath = el; } if (!privateMethodPath && el.isClassPrivateMethod()) { privateMethodPath = el; } if (!staticBlockPath && el.isStaticBlock()) { staticBlockPath = el; } } if (decoratorPath && privateFieldPath) { throw privateFieldPath.buildCodeFrameError( "Private fields in decorated classes are not supported yet.", ); } if (decoratorPath && privateMethodPath) { throw privateMethodPath.buildCodeFrameError( "Private methods in decorated classes are not supported yet.", ); } if (decoratorPath && !hasFeature(file, FEATURES.decorators)) { throw path.buildCodeFrameError( "Decorators are not enabled." + "\nIf you are using " + '["@babel/plugin-proposal-decorators", { "version": "legacy" }], ' + 'make sure it comes *before* "@babel/plugin-transform-class-properties" ' + "and enable loose mode, like so:\n" + '\t["@babel/plugin-proposal-decorators", { "version": "legacy" }]\n' + '\t["@babel/plugin-transform-class-properties", { "loose": true }]', ); } if (privateMethodPath && !hasFeature(file, FEATURES.privateMethods)) { throw privateMethodPath.buildCodeFrameError( "Class private methods are not enabled. " + "Please add `@babel/plugin-transform-private-methods` to your configuration.", ); } if ( (publicFieldPath || privateFieldPath) && !hasFeature(file, FEATURES.fields) && // We want to allow enabling the private-methods plugin even without enabling // the class-properties plugin. Class fields will still be compiled in classes // that contain private methods. // This is already allowed with the other various class features plugins, but // it's because they can fallback to a transform separated from this helper. !hasFeature(file, FEATURES.privateMethods) ) { throw path.buildCodeFrameError( "Class fields are not enabled. " + "Please add `@babel/plugin-transform-class-properties` to your configuration.", ); } if (staticBlockPath && !hasFeature(file, FEATURES.staticBlocks)) { throw path.buildCodeFrameError( "Static class blocks are not enabled. " + "Please add `@babel/plugin-transform-class-static-block` to your configuration.", ); } if (decoratorPath || privateMethodPath || staticBlockPath) { // If one of those feature is used we know that its transform is // enabled, otherwise the previous checks throw. return true; } if ( (publicFieldPath || privateFieldPath) && hasFeature(file, FEATURES.fields) ) { return true; } return false; }