From 360223a2ff59b3d1a1c36c0d8046f8d0fff937ab Mon Sep 17 00:00:00 2001 From: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com> Date: Tue, 8 Apr 2025 01:21:05 +0200 Subject: [PATCH] web: Flesh out configs. (#13801) --- .gitignore | 1 + .../@types/eslint-plugin-react-hooks.d.ts | 11 ++ .../@types/eslint-plugin-react.d.ts | 11 ++ packages/eslint-config/LICENSE.txt | 18 +++ packages/eslint-config/README.md | 5 + packages/eslint-config/index.js | 72 +++++++++ packages/eslint-config/javascript-config.js | 143 ++++++++++++++++++ packages/eslint-config/package.json | 53 +++++++ packages/eslint-config/react-config.js | 34 +++++ packages/eslint-config/tsconfig.json | 8 + packages/eslint-config/typescript-config.js | 35 +++++ packages/monorepo/LICENSE.txt | 18 +++ packages/monorepo/README.md | 5 + packages/monorepo/constants.js | 17 +++ packages/monorepo/index.js | 4 + packages/monorepo/package.json | 19 +++ packages/monorepo/paths.js | 30 ++++ packages/monorepo/scripting.js | 40 +++++ packages/monorepo/scripts.js | 0 packages/monorepo/tsconfig.json | 9 ++ packages/monorepo/version.js | 45 ++++++ packages/prettier-config/LICENSE.txt | 18 +++ packages/prettier-config/README.md | 5 + packages/prettier-config/config.js | 80 ++++++++++ packages/prettier-config/format.js | 20 +++ packages/prettier-config/index.js | 6 + packages/prettier-config/package.json | 27 ++++ packages/prettier-config/tsconfig.json | 8 + packages/tsconfig/LICENSE.txt | 18 +++ packages/tsconfig/README.md | 6 + packages/tsconfig/package.json | 18 +++ packages/tsconfig/tsconfig.json | 29 ++++ 32 files changed, 813 insertions(+) create mode 100644 packages/eslint-config/@types/eslint-plugin-react-hooks.d.ts create mode 100644 packages/eslint-config/@types/eslint-plugin-react.d.ts create mode 100644 packages/eslint-config/LICENSE.txt create mode 100644 packages/eslint-config/README.md create mode 100644 packages/eslint-config/index.js create mode 100644 packages/eslint-config/javascript-config.js create mode 100644 packages/eslint-config/package.json create mode 100644 packages/eslint-config/react-config.js create mode 100644 packages/eslint-config/tsconfig.json create mode 100644 packages/eslint-config/typescript-config.js create mode 100644 packages/monorepo/LICENSE.txt create mode 100644 packages/monorepo/README.md create mode 100644 packages/monorepo/constants.js create mode 100644 packages/monorepo/index.js create mode 100644 packages/monorepo/package.json create mode 100644 packages/monorepo/paths.js create mode 100644 packages/monorepo/scripting.js create mode 100644 packages/monorepo/scripts.js create mode 100644 packages/monorepo/tsconfig.json create mode 100644 packages/monorepo/version.js create mode 100644 packages/prettier-config/LICENSE.txt create mode 100644 packages/prettier-config/README.md create mode 100644 packages/prettier-config/config.js create mode 100644 packages/prettier-config/format.js create mode 100644 packages/prettier-config/index.js create mode 100644 packages/prettier-config/package.json create mode 100644 packages/prettier-config/tsconfig.json create mode 100644 packages/tsconfig/LICENSE.txt create mode 100644 packages/tsconfig/README.md create mode 100644 packages/tsconfig/package.json create mode 100644 packages/tsconfig/tsconfig.json diff --git a/.gitignore b/.gitignore index 48a3822b95..d3473e576e 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ eggs/ lib64/ parts/ dist/ +out/ sdist/ var/ wheels/ diff --git a/packages/eslint-config/@types/eslint-plugin-react-hooks.d.ts b/packages/eslint-config/@types/eslint-plugin-react-hooks.d.ts new file mode 100644 index 0000000000..4524d3698d --- /dev/null +++ b/packages/eslint-config/@types/eslint-plugin-react-hooks.d.ts @@ -0,0 +1,11 @@ +/** + * @file TypeScript type definitions for eslint-plugin-react-hooks + */ +declare module "eslint-plugin-react-hooks" { + import { ESLint } from "eslint"; + // We have to do this because ESLint aliases the namespace and class simultaneously. + type PluginInstance = ESLint.Plugin; + const Plugin: PluginInstance; + + export default Plugin; +} diff --git a/packages/eslint-config/@types/eslint-plugin-react.d.ts b/packages/eslint-config/@types/eslint-plugin-react.d.ts new file mode 100644 index 0000000000..1aa8291941 --- /dev/null +++ b/packages/eslint-config/@types/eslint-plugin-react.d.ts @@ -0,0 +1,11 @@ +/** + * @file TypeScript type definitions for eslint-plugin-react + */ +declare module "eslint-plugin-react" { + import { ESLint } from "eslint"; + // We have to do this because ESLint aliases the namespace and class simultaneously. + type PluginInstance = ESLint.Plugin; + const Plugin: PluginInstance; + + export default Plugin; +} diff --git a/packages/eslint-config/LICENSE.txt b/packages/eslint-config/LICENSE.txt new file mode 100644 index 0000000000..33b9c1516e --- /dev/null +++ b/packages/eslint-config/LICENSE.txt @@ -0,0 +1,18 @@ +The MIT License (MIT) + +Copyright (c) 2025 Authentik Security, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES +OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/eslint-config/README.md b/packages/eslint-config/README.md new file mode 100644 index 0000000000..d22123853a --- /dev/null +++ b/packages/eslint-config/README.md @@ -0,0 +1,5 @@ +# `@goauthentik/eslint-config` + +This package contains the ESLint configuration used by authentik. +While it is possible to use this configuration outside of our projects, +you may find that it is not as useful as other popular configurations. diff --git a/packages/eslint-config/index.js b/packages/eslint-config/index.js new file mode 100644 index 0000000000..05dff0da67 --- /dev/null +++ b/packages/eslint-config/index.js @@ -0,0 +1,72 @@ +import eslint from "@eslint/js"; +import { javaScriptConfig } from "@goauthentik/eslint-config/javascript-config"; +import { reactConfig } from "@goauthentik/eslint-config/react-config"; +import { typescriptConfig } from "@goauthentik/eslint-config/typescript-config"; +import * as litconf from "eslint-plugin-lit"; +import * as wcconf from "eslint-plugin-wc"; +import tseslint from "typescript-eslint"; + +// @ts-check + +/** + * @typedef ESLintPackageConfigOptions Options for creating package ESLint configuration. + * @property {string[]} [ignorePatterns] Override ignore patterns for ESLint. + */ + +/** + * @type {string[]} Default Ignore patterns for ESLint. + */ +export const DefaultIgnorePatterns = [ + // --- + "**/*.md", + "**/out", + "**/dist", + "**/.wireit", + "website/build/**", + "website/.docusaurus/**", + "**/node_modules", + "**/coverage", + "**/storybook-static", + "**/locale-codes.ts", + "**/src/locales", + "**/gen-ts-api", +]; + +/** + * Given a preferred package name, creates a ESLint configuration object. + * + * @param {ESLintPackageConfigOptions} options The preferred package configuration options. + * + * @returns The ESLint configuration object. + */ +export function createESLintPackageConfig({ ignorePatterns = DefaultIgnorePatterns } = {}) { + return tseslint.config( + { + ignores: ignorePatterns, + }, + + eslint.configs.recommended, + javaScriptConfig, + + wcconf.configs["flat/recommended"], + litconf.configs["flat/recommended"], + + ...tseslint.configs.recommended, + + ...typescriptConfig, + + ...reactConfig, + + { + rules: { + "no-console": "off", + }, + files: [ + // --- + "**/scripts/**/*", + "**/test/**/*", + "**/tests/**/*", + ], + }, + ); +} diff --git a/packages/eslint-config/javascript-config.js b/packages/eslint-config/javascript-config.js new file mode 100644 index 0000000000..039fa3b560 --- /dev/null +++ b/packages/eslint-config/javascript-config.js @@ -0,0 +1,143 @@ +// @ts-check +import tseslint from "typescript-eslint"; + +const MAX_DEPTH = 4; +const MAX_NESTED_CALLBACKS = 4; +const MAX_PARAMS = 5; + +/** + * ESLint configuration for JavaScript authentik projects. + */ +export const javaScriptConfig = tseslint.config({ + rules: { + // TODO: Clean up before enabling. + "accessor-pairs": "off", + "array-callback-return": "error", + "block-scoped-var": "error", + "consistent-return": ["error", { treatUndefinedAsUnspecified: false }], + "consistent-this": ["error", "that"], + "curly": "off", + "dot-notation": [ + "error", + { + allowKeywords: true, + }, + ], + "eqeqeq": "error", + "func-names": ["error", "as-needed"], + "guard-for-in": "error", + "max-depth": ["error", MAX_DEPTH], + "max-nested-callbacks": ["error", MAX_NESTED_CALLBACKS], + "max-params": ["error", MAX_PARAMS], + // TODO: Clean up before enabling. + // "new-cap": "error", + "no-alert": "error", + "no-array-constructor": "error", + "no-bitwise": [ + "error", + { + allow: ["~"], + int32Hint: true, + }, + ], + "no-caller": "error", + "no-case-declarations": "error", + "no-class-assign": "error", + "no-cond-assign": "error", + "no-const-assign": "error", + "no-constant-condition": "error", + "no-control-regex": "error", + "no-debugger": "error", + "no-delete-var": "error", + "no-div-regex": "error", + "no-dupe-args": "error", + "no-dupe-keys": "error", + "no-duplicate-case": "error", + "no-else-return": "error", + "no-empty": "error", + "no-empty-character-class": "error", + "no-empty-function": ["error", { allow: ["constructors"] }], + "no-labels": "error", + "no-eq-null": "error", + "no-eval": "error", + "no-ex-assign": "error", + "no-extend-native": "error", + "no-extra-bind": "error", + "no-extra-boolean-cast": "error", + "no-extra-label": "error", + "no-fallthrough": "error", + "no-func-assign": "error", + "no-implied-eval": "error", + "no-implicit-coercion": "error", + "no-implicit-globals": "error", + "no-inner-declarations": ["error", "functions"], + "no-invalid-regexp": "error", + "no-irregular-whitespace": "error", + "no-iterator": "error", + "no-label-var": "error", + "no-lone-blocks": "error", + "no-lonely-if": "error", + "no-loop-func": "error", + "no-multi-str": "error", + // TODO: Clean up before enabling. + "no-negated-condition": "off", + "no-new": "error", + "no-new-func": "error", + "no-new-wrappers": "error", + "no-obj-calls": "error", + "no-octal": "error", + "no-octal-escape": "error", + "no-param-reassign": ["error", { props: false }], + "no-proto": "error", + "no-redeclare": "error", + "no-regex-spaces": "error", + "no-restricted-syntax": ["error", "WithStatement"], + "no-script-url": "error", + "no-self-assign": "error", + "no-self-compare": "error", + "no-sequences": "error", + // TODO: Clean up before enabling. + // "no-shadow": "error", + "no-shadow-restricted-names": "error", + "no-sparse-arrays": "error", + "no-this-before-super": "error", + "no-throw-literal": "error", + "no-trailing-spaces": "off", // Handled by Prettier. + "no-undef": "off", + "no-undef-init": "off", + "no-unexpected-multiline": "error", + "no-useless-constructor": "error", + "no-unmodified-loop-condition": "error", + "no-unneeded-ternary": "error", + "no-unreachable": "error", + "no-unused-expressions": "error", + "no-unused-labels": "error", + "no-use-before-define": "error", + "no-useless-call": "error", + "no-dupe-class-members": "error", + "no-var": "error", + "no-void": "error", + "no-with": "error", + "prefer-arrow-callback": "error", + "prefer-const": "error", + "prefer-rest-params": "error", + "prefer-spread": "error", + "prefer-template": "error", + "radix": "error", + "require-yield": "error", + "strict": ["error", "global"], + "use-isnan": "error", + "valid-typeof": "error", + "vars-on-top": "error", + "yoda": ["error", "never"], + + "no-console": ["error", { allow: ["debug", "warn", "error"] }], + // SonarJS is not yet compatible with ESLint 9. Commenting these out + // until it is. + // "sonarjs/cognitive-complexity": ["off", MAX_COGNITIVE_COMPLEXITY], + // "sonarjs/no-duplicate-string": "off", + // "sonarjs/no-nested-template-literals": "off", + }, +}); + +export default javaScriptConfig; diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json new file mode 100644 index 0000000000..f2101eb850 --- /dev/null +++ b/packages/eslint-config/package.json @@ -0,0 +1,53 @@ +{ + "name": "@goauthentik/eslint-config", + "version": "1.0.0", + "description": "authentik's ESLint config", + "license": "MIT", + "type": "module", + "exports": { + "./package.json": "./package.json", + ".": { + "import": "./index.js", + "types": "./out/index.d.ts" + }, + "./react-config": { + "import": "./react-config.js", + "types": "./out/react-config.d.ts" + }, + "./javascript-config": { + "import": "./javascript-config.js", + "types": "./out/javascript-config.d.ts" + }, + "./typescript-config": { + "import": "./typescript-config.js", + "types": "./out/typescript-config.d.ts" + } + }, + "dependencies": { + "eslint": "^9.23.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-react": "^7.37.4", + "eslint-plugin-react-hooks": "^5.2.0" + }, + "devDependencies": { + "@goauthentik/tsconfig": "1.0.0", + "@types/eslint": "^9.6.1", + "typescript": "^5.8.2", + "typescript-eslint": "^8.29.0" + }, + "peerDependencies": { + "typescript": "^5.8.2", + "typescript-eslint": "^8.29.0" + }, + "optionalDependencies": { + "react": "^18.3.1" + }, + "engines": { + "node": ">=20.11" + }, + "types": "./out/index.d.ts", + "prettier": "@goauthentik/prettier-config", + "publishConfig": { + "access": "public" + } +} diff --git a/packages/eslint-config/react-config.js b/packages/eslint-config/react-config.js new file mode 100644 index 0000000000..a47922cb98 --- /dev/null +++ b/packages/eslint-config/react-config.js @@ -0,0 +1,34 @@ +import reactPlugin from "eslint-plugin-react"; +import hooksPlugin from "eslint-plugin-react-hooks"; +import tseslint from "typescript-eslint"; + +/** + * ESLint configuration for React authentik projects. + */ +export const reactConfig = tseslint.config({ + settings: { + react: { + version: "detect", + }, + }, + + plugins: { + "react": reactPlugin, + "react-hooks": hooksPlugin, + }, + + rules: { + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "warn", + + "react/jsx-uses-react": 0, + + "react/display-name": "off", + "react/jsx-curly-brace-presence": "error", + "react/jsx-no-leaked-render": "error", + "react/prop-types": "off", + "react/react-in-jsx-scope": "off", + }, +}); + +export default reactConfig; diff --git a/packages/eslint-config/tsconfig.json b/packages/eslint-config/tsconfig.json new file mode 100644 index 0000000000..d0a26a13bc --- /dev/null +++ b/packages/eslint-config/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@goauthentik/tsconfig", + "compilerOptions": { + "baseUrl": ".", + "checkJs": true, + "emitDeclarationOnly": true + } +} diff --git a/packages/eslint-config/typescript-config.js b/packages/eslint-config/typescript-config.js new file mode 100644 index 0000000000..c14b99221e --- /dev/null +++ b/packages/eslint-config/typescript-config.js @@ -0,0 +1,35 @@ +// @ts-check +import tseslint from "typescript-eslint"; + +/** + * ESLint configuration for TypeScript authentik projects. + */ +export const typescriptConfig = tseslint.config({ + rules: { + "@typescript-eslint/ban-ts-comment": [ + "error", + { + "ts-expect-error": "allow-with-description", + "ts-ignore": true, + "ts-nocheck": "allow-with-description", + "ts-check": false, + "minimumDescriptionLength": 5, + }, + ], + "no-use-before-define": "off", + "@typescript-eslint/no-use-before-define": "error", + "no-invalid-this": "off", + "no-unused-vars": "off", + "@typescript-eslint/no-namespace": "off", + "@typescript-eslint/no-unused-vars": [ + "warn", + { + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + caughtErrorsIgnorePattern: "^_", + }, + ], + }, +}); + +export default typescriptConfig; diff --git a/packages/monorepo/LICENSE.txt b/packages/monorepo/LICENSE.txt new file mode 100644 index 0000000000..33b9c1516e --- /dev/null +++ b/packages/monorepo/LICENSE.txt @@ -0,0 +1,18 @@ +The MIT License (MIT) + +Copyright (c) 2025 Authentik Security, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES +OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/monorepo/README.md b/packages/monorepo/README.md new file mode 100644 index 0000000000..533a60093c --- /dev/null +++ b/packages/monorepo/README.md @@ -0,0 +1,5 @@ +# `@goauthentik/monorepo` + +This package contains utility scripts common to all TypeScript and JavaScript packages in the +`@goauthentik` monorepo. + diff --git a/packages/monorepo/constants.js b/packages/monorepo/constants.js new file mode 100644 index 0000000000..e091abeb2c --- /dev/null +++ b/packages/monorepo/constants.js @@ -0,0 +1,17 @@ +/** + * @file Constants for JavaScript and TypeScript files. + * + */ + +/** + * The current Node.js environment, defaulting to "development" when not set. + * + * Note, this should only be used during the build process. + * + * If you need to check the environment at runtime, use `process.env.NODE_ENV` to + * ensure that module tree-shaking works correctly. + * + */ +export const NodeEnvironment = /** @type {'development' | 'production'} */ ( + process.env.NODE_ENV || "development" +); diff --git a/packages/monorepo/index.js b/packages/monorepo/index.js new file mode 100644 index 0000000000..6c0af406bd --- /dev/null +++ b/packages/monorepo/index.js @@ -0,0 +1,4 @@ +export * from "./paths.js"; +export * from "./constants.js"; +export * from "./version.js"; +export * from "./scripting.js"; diff --git a/packages/monorepo/package.json b/packages/monorepo/package.json new file mode 100644 index 0000000000..3c801e6682 --- /dev/null +++ b/packages/monorepo/package.json @@ -0,0 +1,19 @@ +{ + "name": "@goauthentik/monorepo", + "version": "1.0.0", + "description": "Utilities for the authentik monorepo.", + "private": true, + "license": "MIT", + "type": "module", + "exports": { + "./package.json": "./package.json", + ".": { + "import": "./index.js", + "types": "./out/index.d.ts" + } + }, + "types": "./out/index.d.ts", + "engines": { + "node": ">=20.11" + } +} diff --git a/packages/monorepo/paths.js b/packages/monorepo/paths.js new file mode 100644 index 0000000000..adade5d2c3 --- /dev/null +++ b/packages/monorepo/paths.js @@ -0,0 +1,30 @@ +import { createRequire } from "node:module"; +import { dirname, join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +/** + * @typedef {'~authentik'} MonoRepoRoot + */ + +/** + * The root of the authentik monorepo. + */ +export const MonoRepoRoot = /** @type {MonoRepoRoot} */ (resolve(__dirname, "..", "..")); + +const require = createRequire(import.meta.url); + +/** + * Resolve a package name to its location in the monorepo to the single node_modules directory. + * @param {string} packageName + * @returns {string} The resolved path to the package. + * @throws {Error} If the package cannot be resolved. + */ +export function resolvePackage(packageName) { + const packageJSONPath = require.resolve(join(packageName, "package.json"), { + paths: [MonoRepoRoot], + }); + + return dirname(packageJSONPath); +} diff --git a/packages/monorepo/scripting.js b/packages/monorepo/scripting.js new file mode 100644 index 0000000000..6e260d5287 --- /dev/null +++ b/packages/monorepo/scripting.js @@ -0,0 +1,40 @@ +import { createRequire } from "node:module"; +import * as path from "node:path"; +import * as process from "node:process"; +import { fileURLToPath } from "node:url"; + +/** + * Predicate to determine if a module was run directly, i.e. not imported. + * + * @param {ImportMeta} meta The `import.meta` object of the module. + * + * @return {boolean} Whether the module was run directly. + */ +export function isMain(meta) { + // Are we not in a module context? + if (!meta) return false; + + const relativeScriptPath = process.argv[1]; + + if (!relativeScriptPath) return false; + + const require = createRequire(meta.url); + const absoluteScriptPath = require.resolve(relativeScriptPath); + + const modulePath = fileURLToPath(meta.url); + + const scriptExtension = path.extname(absoluteScriptPath); + + if (scriptExtension) { + return modulePath === absoluteScriptPath; + } + + const moduleExtension = path.extname(modulePath); + + if (moduleExtension) { + return absoluteScriptPath === modulePath.slice(0, -moduleExtension.length); + } + + // If both are without extension, compare them directly. + return modulePath === absoluteScriptPath; +} diff --git a/packages/monorepo/scripts.js b/packages/monorepo/scripts.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/monorepo/tsconfig.json b/packages/monorepo/tsconfig.json new file mode 100644 index 0000000000..83e94d8cf2 --- /dev/null +++ b/packages/monorepo/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@goauthentik/tsconfig", + "compilerOptions": { + "resolveJsonModule": true, + "baseUrl": ".", + "checkJs": true, + "emitDeclarationOnly": true + } +} diff --git a/packages/monorepo/version.js b/packages/monorepo/version.js new file mode 100644 index 0000000000..ba8ab6a345 --- /dev/null +++ b/packages/monorepo/version.js @@ -0,0 +1,45 @@ +import { execSync } from "node:child_process"; + +import PackageJSON from "../../package.json" with { type: "json" }; +import { MonoRepoRoot } from "./paths.js"; + +/** + * The current version of authentik in SemVer format. + * + */ +export const AuthentikVersion = /**@type {`${number}.${number}.${number}`} */ (PackageJSON.version); + +/** + * Reads the last commit hash from the current git repository. + */ +export function readGitBuildHash() { + try { + const commit = execSync("git rev-parse HEAD", { + encoding: "utf8", + cwd: MonoRepoRoot, + }) + .toString() + .trim(); + + return commit; + } catch (_error) { + console.debug("Git commit could not be read."); + } + + return process.env.GIT_BUILD_HASH || ""; +} + +/** + * Reads the build identifier for the current environment. + * + * This must match the behavior defined in authentik's server-side `get_full_version` function. + * + * @see {@link "authentik\_\_init\_\_.py"} + */ +export function readBuildIdentifier() { + const { GIT_BUILD_HASH } = process.env; + + if (!GIT_BUILD_HASH) return AuthentikVersion; + + return [AuthentikVersion, GIT_BUILD_HASH].join("+"); +} diff --git a/packages/prettier-config/LICENSE.txt b/packages/prettier-config/LICENSE.txt new file mode 100644 index 0000000000..33b9c1516e --- /dev/null +++ b/packages/prettier-config/LICENSE.txt @@ -0,0 +1,18 @@ +The MIT License (MIT) + +Copyright (c) 2025 Authentik Security, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES +OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/prettier-config/README.md b/packages/prettier-config/README.md new file mode 100644 index 0000000000..f4a2077469 --- /dev/null +++ b/packages/prettier-config/README.md @@ -0,0 +1,5 @@ +# `@goauthentik/prettier-config` + +This package contains the Prettier configuration used by authentik. +While it is possible to use this configuration outside of our projects, +you may find that it is not as useful as other popular configurations. diff --git a/packages/prettier-config/config.js b/packages/prettier-config/config.js new file mode 100644 index 0000000000..1f5f9fec1d --- /dev/null +++ b/packages/prettier-config/config.js @@ -0,0 +1,80 @@ +/** + * @file Prettier configuration for authentik. + * + * @import { Config as PrettierConfig } from "prettier"; + * @import { PluginConfig as SortPluginConfig } from "@trivago/prettier-plugin-sort-imports"; + * + * @typedef {object} PackageJSONPluginConfig + * @property {string[]} [packageSortOrder] Custom ordering array. + */ + +/** + * authentik Prettier configuration. + * + * @type {PrettierConfig & SortPluginConfig & PackageJSONPluginConfig} + * @internal + */ +export const AuthentikPrettierConfig = { + arrowParens: "always", + bracketSpacing: true, + embeddedLanguageFormatting: "auto", + htmlWhitespaceSensitivity: "css", + insertPragma: false, + jsxSingleQuote: false, + printWidth: 100, + proseWrap: "preserve", + quoteProps: "consistent", + requirePragma: false, + semi: true, + singleQuote: false, + tabWidth: 4, + trailingComma: "all", + useTabs: false, + vueIndentScriptAndStyle: false, + plugins: ["prettier-plugin-packagejson", "@trivago/prettier-plugin-sort-imports"], + importOrder: ["^(@?)lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"], + importOrderSeparation: true, + importOrderSortSpecifiers: true, + importOrderParserPlugins: ["typescript", "jsx", "classProperties", "decorators-legacy"], + overrides: [ + { + files: "schemas/**/*.json", + options: { + tabWidth: 2, + }, + }, + { + files: "tsconfig.json", + options: { + trailingComma: "none", + }, + }, + { + files: "package.json", + options: { + packageSortOrder: [ + // --- + "name", + "version", + "description", + "license", + "private", + "author", + "authors", + "scripts", + "main", + "type", + "exports", + "imports", + "dependencies", + "devDependencies", + "peerDependencies", + "optionalDependencies", + "wireit", + "resolutions", + "engines", + ], + }, + }, + ], +}; diff --git a/packages/prettier-config/format.js b/packages/prettier-config/format.js new file mode 100644 index 0000000000..035689342f --- /dev/null +++ b/packages/prettier-config/format.js @@ -0,0 +1,20 @@ +import { format } from "prettier"; + +import { AuthentikPrettierConfig } from "./config.js"; + +/** + * Format using Prettier. + * + * Defaults to using the TypeScript parser. + * + * @category Formatting + * @param {string} fileContents The contents of the file to format. + * + * @returns {Promise} The formatted file contents. + */ +export function formatWithPrettier(fileContents) { + return format(fileContents, { + ...AuthentikPrettierConfig, + parser: "typescript", + }); +} diff --git a/packages/prettier-config/index.js b/packages/prettier-config/index.js new file mode 100644 index 0000000000..8a6bd459c8 --- /dev/null +++ b/packages/prettier-config/index.js @@ -0,0 +1,6 @@ +import { AuthentikPrettierConfig } from "./config.js"; + +export * from "./config.js"; +export * from "./format.js"; + +export default AuthentikPrettierConfig; diff --git a/packages/prettier-config/package.json b/packages/prettier-config/package.json new file mode 100644 index 0000000000..bece697b24 --- /dev/null +++ b/packages/prettier-config/package.json @@ -0,0 +1,27 @@ +{ + "name": "@goauthentik/prettier-config", + "version": "1.0.0", + "description": "authentik's Prettier config", + "license": "MIT", + "type": "module", + "exports": { + "./package.json": "./package.json", + ".": { + "import": "./index.js", + "types": "./out/index.d.ts" + } + }, + "types": "./out/index.d.ts", + "peerDependencies": { + "@trivago/prettier-plugin-sort-imports": "^5.2.2", + "prettier": "^3.5.3", + "prettier-plugin-organize-imports": "^4.1.0", + "prettier-plugin-packagejson": "^2.5.10" + }, + "engines": { + "node": ">=20.11" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/prettier-config/tsconfig.json b/packages/prettier-config/tsconfig.json new file mode 100644 index 0000000000..d0a26a13bc --- /dev/null +++ b/packages/prettier-config/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@goauthentik/tsconfig", + "compilerOptions": { + "baseUrl": ".", + "checkJs": true, + "emitDeclarationOnly": true + } +} diff --git a/packages/tsconfig/LICENSE.txt b/packages/tsconfig/LICENSE.txt new file mode 100644 index 0000000000..33b9c1516e --- /dev/null +++ b/packages/tsconfig/LICENSE.txt @@ -0,0 +1,18 @@ +The MIT License (MIT) + +Copyright (c) 2025 Authentik Security, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES +OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/tsconfig/README.md b/packages/tsconfig/README.md new file mode 100644 index 0000000000..ba5001b212 --- /dev/null +++ b/packages/tsconfig/README.md @@ -0,0 +1,6 @@ +# `@goauthentik/tsconfig` + +This package contains the TypeScript configuration used by authentik TypeScript projects. + +While it is possible to use this configuration outside of our projects, +you may find that it is not as useful as other popular configurations. diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json new file mode 100644 index 0000000000..11941efeab --- /dev/null +++ b/packages/tsconfig/package.json @@ -0,0 +1,18 @@ +{ + "name": "@goauthentik/tsconfig", + "version": "1.0.0", + "description": "authentik's s base TypeScript configuration.", + "keywords": [ + "tsconfig", + "typescript" + ], + "license": "MIT", + "type": "module", + "main": "tsconfig.json", + "engines": { + "node": ">=20.11" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/tsconfig/tsconfig.json b/packages/tsconfig/tsconfig.json new file mode 100644 index 0000000000..2f3f1e9584 --- /dev/null +++ b/packages/tsconfig/tsconfig.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "alwaysStrict": true, + "baseUrl": ".", + "composite": true, + "declaration": true, + "declarationMap": true, + "esModuleInterop": false, + "isolatedModules": true, + "incremental": true, + "jsx": "react-jsx", + "lib": ["ESNext"], + "module": "NodeNext", + "moduleResolution": "NodeNext", + "newLine": "lf", + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": false, + "outDir": "${configDir}/out", + "pretty": true, + "skipDefaultLibCheck": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "noUncheckedIndexedAccess": true, + "target": "ESNext", + "useUnknownInCatchVariables": true + } +}