core: Format. Fix. WIP.
This commit is contained in:
		| @ -1,72 +1,103 @@ | ||||
| import { spawnSync } from "child_process"; | ||||
| import fs from "fs"; | ||||
| import path from "path"; | ||||
| import process from "process"; | ||||
| /** | ||||
|  * @file Lit Localize build script. | ||||
|  * | ||||
|  * @import { Config } from "@lit/localize-tools/lib/types/config.js" | ||||
|  */ | ||||
| import * as fs from "node:fs/promises"; | ||||
| import * as path from "node:path"; | ||||
| import process from "node:process"; | ||||
| import { $ } from "zx"; | ||||
|  | ||||
| const localizeRules = await import("../lit-localize.json", { | ||||
|     with: { | ||||
|         type: "json", | ||||
|     }, | ||||
| }) | ||||
|     .then((module) => { | ||||
|         return /** @type {Config} */ (module.default); | ||||
|     }) | ||||
|  | ||||
|     .catch((error) => { | ||||
|         console.error("Failed to load lit-localize.json", error); | ||||
|         process.exit(1); | ||||
|     }); | ||||
|  | ||||
| /** | ||||
|  * Determines if all the Xliff translation source files are present and if the Typescript source | ||||
|  * files generated from those sources are up-to-date. If they are not, it runs the locale building | ||||
|  * script, intercepting the long spew of "this string is not translated" and replacing it with a | ||||
|  * summary of how many strings are missing with respect to the source locale. | ||||
|  * Attempt to stat a file, returning null if it doesn't exist. | ||||
|  */ | ||||
| function tryStat(filePath) { | ||||
|     return fs.stat(filePath).catch(() => null); | ||||
| } | ||||
|  | ||||
| const localizeRules = JSON.parse(fs.readFileSync("./lit-localize.json", "utf-8")); | ||||
| /** | ||||
|  * Check if a generated file is up-to-date with its XLIFF source. | ||||
|  * | ||||
|  * @param {string} languageCode The locale to check. | ||||
|  */ | ||||
| async function generatedFileIsUpToDateWithXliffSource(languageCode) { | ||||
|     const xlfFilePath = path.join("./xliff", `${languageCode}.xlf`); | ||||
|     const xlfStat = await tryStat(xlfFilePath); | ||||
|  | ||||
| function generatedFileIsUpToDateWithXliffSource(loc) { | ||||
|     const xliff = path.join("./xliff", `${loc}.xlf`); | ||||
|     const gened = path.join("./src/locales", `${loc}.ts`); | ||||
|     if (!xlfStat) { | ||||
|         console.error(`lit-localize expected '${languageCode}.xlf', but XLF file is not present`); | ||||
|  | ||||
|     // Returns false if: the expected XLF file doesn't exist, The expected | ||||
|     // generated file doesn't exist, or the XLF file is newer (has a higher date) | ||||
|     // than the generated file.  The missing XLF file is important enough it | ||||
|     // generates a unique error message and halts the build. | ||||
|  | ||||
|     try { | ||||
|         var xlfStat = fs.statSync(xliff); | ||||
|     } catch (_error) { | ||||
|         console.error(`lit-localize expected '${loc}.xlf', but XLF file is not present`); | ||||
|         process.exit(1); | ||||
|     } | ||||
|  | ||||
|     // If the generated file doesn't exist, of course it's not up to date. | ||||
|     try { | ||||
|         var genedStat = fs.statSync(gened); | ||||
|     } catch (_error) { | ||||
|         return false; | ||||
|     const generatedTSFilePath = path.join("./src/locales", `${languageCode}.ts`); | ||||
|  | ||||
|     const generatedTSFilePathStat = await tryStat(generatedTSFilePath); | ||||
|  | ||||
|     // Does the generated file exist? | ||||
|     if (!generatedTSFilePathStat) { | ||||
|         return { | ||||
|             languageCode, | ||||
|             exists: false, | ||||
|             expired: null, | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     // if the generated file is the same age or newer (date is greater) than the xliff file, it's | ||||
|     // presumed to have been generated by that file and is up-to-date. | ||||
|     return genedStat.mtimeMs >= xlfStat.mtimeMs; | ||||
|     return { | ||||
|         languageCode, | ||||
|         exists: true, | ||||
|         // Is the generated file older than the XLIFF file? | ||||
|         expired: generatedTSFilePathStat.mtimeMs < xlfStat.mtimeMs, | ||||
|     }; | ||||
| } | ||||
|  | ||||
| // For all the expected files, find out if any aren't up-to-date. | ||||
| const upToDate = localizeRules.targetLocales.reduce( | ||||
|     (acc, loc) => acc && generatedFileIsUpToDateWithXliffSource(loc), | ||||
|     true, | ||||
| const results = await Promise.all( | ||||
|     localizeRules.targetLocales.map(generatedFileIsUpToDateWithXliffSource), | ||||
| ); | ||||
|  | ||||
| if (!upToDate) { | ||||
|     const status = spawnSync("npm", ["run", "build-locales:build"], { encoding: "utf8" }); | ||||
| const pendingBuild = results.some((result) => !result.exists || result.expired); | ||||
|  | ||||
|     // Count all the missing message warnings | ||||
|     const counts = status.stderr.split("\n").reduce((acc, line) => { | ||||
|         const match = /^([\w-]+) message/.exec(line); | ||||
|         if (!match) { | ||||
|             return acc; | ||||
|         } | ||||
|         acc.set(match[1], (acc.get(match[1]) || 0) + 1); | ||||
|         return acc; | ||||
|     }, new Map()); | ||||
|  | ||||
|     const locales = Array.from(counts.keys()); | ||||
|     locales.sort(); | ||||
|  | ||||
|     const report = locales | ||||
|         .map((locale) => `Locale '${locale}' has ${counts.get(locale)} missing translations`) | ||||
|         .join("\n"); | ||||
|  | ||||
|     console.log(`Translation tables rebuilt.\n${report}\n`); | ||||
| if (!pendingBuild) { | ||||
|     console.log("Local is up-to-date!"); | ||||
|     process.exit(0); | ||||
| } | ||||
|  | ||||
| console.log("Locale ./src is up-to-date"); | ||||
| const status = await $({ stdio: ["ignore", "pipe", "pipe"] })`npx lit-localize build`; | ||||
|  | ||||
| /** | ||||
|  * @type {Map<string, number>} | ||||
|  */ | ||||
| const counts = new Map(); | ||||
|  | ||||
| // Count all the missing message warnings | ||||
| for (const line of status.stderr.split("\n")) { | ||||
|     const match = /^([\w-]+) message/.exec(line); | ||||
|     if (!match) continue; | ||||
|  | ||||
|     const count = counts.get(match[1]) || 0; | ||||
|     counts.set(match[1], count + 1); | ||||
| } | ||||
|  | ||||
| const locales = Array.from(counts.keys()).sort(); | ||||
|  | ||||
| for (const locale of locales) { | ||||
|     console.log(`Locale '${locale}' has ${counts.get(locale)} missing translations`); | ||||
| } | ||||
|  | ||||
| await $`npx prettier --write src/locale-codes.ts`; | ||||
|  | ||||
| console.log("\nTranslation tables rebuilt.\n"); | ||||
|  | ||||
							
								
								
									
										79
									
								
								web/scripts/build-sfe.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								web/scripts/build-sfe.mjs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | ||||
| /** | ||||
|  * @file Build script for the simplified flow executor (SFE). | ||||
|  */ | ||||
| import { DistDirectory, PackageRoot } from "@goauthentik/web/paths"; | ||||
| import esbuild from "esbuild"; | ||||
| import copy from "esbuild-plugin-copy"; | ||||
| import { es5Plugin } from "esbuild-plugin-es5"; | ||||
| import { createRequire } from "node:module"; | ||||
| import * as path from "node:path"; | ||||
|  | ||||
| const require = createRequire(import.meta.url); | ||||
|  | ||||
| async function buildSFE() { | ||||
|     const sourceDirectory = path.join(PackageRoot, "packages", "sfe"); | ||||
|     const outDirectory = path.join(DistDirectory, "sfe"); | ||||
|  | ||||
|     const bootstrapCSSPath = require.resolve( | ||||
|         path.join("bootstrap", "dist", "css", "bootstrap.min.css"), | ||||
|     ); | ||||
|  | ||||
|     /** | ||||
|      * @type {esbuild.BuildOptions} | ||||
|      */ | ||||
|     const config = { | ||||
|         tsconfig: path.join(sourceDirectory, "tsconfig.json"), | ||||
|         entryPoints: [path.join(sourceDirectory, "index.ts")], | ||||
|         minify: false, | ||||
|         bundle: true, | ||||
|         sourcemap: true, | ||||
|  | ||||
|         legalComments: "external", | ||||
|         platform: "browser", | ||||
|         format: "iife", | ||||
|         alias: { | ||||
|             "@swc/helpers": path.dirname(require.resolve("@swc/helpers/package.json")), | ||||
|         }, | ||||
|         banner: { | ||||
|             js: [ | ||||
|                 // --- | ||||
|                 "// Simplified Flow Executor (SFE)", | ||||
|                 "// @ts-nocheck", | ||||
|                 "", | ||||
|             ].join("\n"), | ||||
|         }, | ||||
|         plugins: [ | ||||
|             copy({ | ||||
|                 assets: [ | ||||
|                     { | ||||
|                         from: bootstrapCSSPath, | ||||
|                         to: outDirectory, | ||||
|                     }, | ||||
|                 ], | ||||
|             }), | ||||
|             es5Plugin({ | ||||
|                 swc: { | ||||
|                     jsc: { | ||||
|                         loose: false, | ||||
|                         externalHelpers: false, | ||||
|                         keepClassNames: false, | ||||
|                     }, | ||||
|                     minify: false, | ||||
|                 }, | ||||
|             }), | ||||
|         ], | ||||
|         target: ["es5"], | ||||
|         outdir: outDirectory, | ||||
|     }; | ||||
|  | ||||
|     esbuild.build(config); | ||||
| } | ||||
|  | ||||
| buildSFE() | ||||
|     .then(() => { | ||||
|         console.log("Build complete"); | ||||
|     }) | ||||
|     .catch((error) => { | ||||
|         console.error("Build failed", error); | ||||
|         process.exit(1); | ||||
|     }); | ||||
| @ -1,14 +1,14 @@ | ||||
| import { execFileSync } from "child_process"; | ||||
| import { deepmerge } from "deepmerge-ts"; | ||||
| import esbuild from "esbuild"; | ||||
| import { polyfillNode } from "esbuild-plugin-polyfill-node"; | ||||
| import findFreePorts from "find-free-ports"; | ||||
| import { copyFileSync, mkdirSync, readFileSync, statSync } from "fs"; | ||||
| import { globSync } from "glob"; | ||||
| import { execFileSync } from "node:child_process"; | ||||
| import { cwd } from "node:process"; | ||||
| import process from "node:process"; | ||||
| import { fileURLToPath } from "node:url"; | ||||
| import * as path from "path"; | ||||
| import { cwd } from "process"; | ||||
| import process from "process"; | ||||
| import { fileURLToPath } from "url"; | ||||
|  | ||||
| import { mdxPlugin } from "./esbuild/build-mdx-plugin.mjs"; | ||||
| import { buildObserverPlugin } from "./esbuild/build-observer-plugin.mjs"; | ||||
| @ -117,6 +117,7 @@ const BASE_ESBUILD_OPTIONS = { | ||||
|     write: true, | ||||
|     sourcemap: true, | ||||
|     minify: NODE_ENV === "production", | ||||
|     legalComments: "external", | ||||
|     splitting: true, | ||||
|     treeShaking: true, | ||||
|     external: ["*.woff", "*.woff2"], | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import { execSync } from "child_process"; | ||||
| import path from "path"; | ||||
| import { execSync } from "node:child_process"; | ||||
| import * as path from "node:path"; | ||||
|  | ||||
| const projectRoot = execSync("git rev-parse --show-toplevel", { encoding: "utf8" }).replace( | ||||
|     "\n", | ||||
|  | ||||
| @ -40,22 +40,13 @@ const name = "mdx-plugin"; | ||||
|  * @returns {Plugin} Plugin. | ||||
|  */ | ||||
| export function mdxPlugin({ root }) { | ||||
|     return { name, setup }; | ||||
|  | ||||
|     /** | ||||
|      * @param {PluginBuild} build | ||||
|      *   Build. | ||||
|      * @returns {undefined} | ||||
|      *   Nothing. | ||||
|      */ | ||||
|     function setup(build) { | ||||
|         build.onLoad({ filter: /\.mdx?$/ }, onload); | ||||
|  | ||||
|         /** | ||||
|          * @param {LoadData} data | ||||
|          *   Data. | ||||
|          * @returns {Promise<OnLoadResult>} | ||||
|          *   Result. | ||||
|          * @param {LoadData} data Data. | ||||
|          * @returns {Promise<OnLoadResult>} Result. | ||||
|          */ | ||||
|         async function onload(data) { | ||||
|             const content = String( | ||||
| @ -77,5 +68,9 @@ export function mdxPlugin({ root }) { | ||||
|                 loader: "file", | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         build.onLoad({ filter: /\.mdx?$/ }, onload); | ||||
|     } | ||||
|  | ||||
|     return { name, setup }; | ||||
| } | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import * as http from "http"; | ||||
| import path from "path"; | ||||
| import * as path from "node:path"; | ||||
|  | ||||
| /** | ||||
|  * Serializes a custom event to a text stream. | ||||
| @ -13,7 +13,7 @@ export function serializeCustomEventToStream(event) { | ||||
|  | ||||
|     const eventContent = [`event: ${event.type}`, `data: ${JSON.stringify(data)}`]; | ||||
|  | ||||
|     return eventContent.join("\n") + "\n\n"; | ||||
|     return `${eventContent.join("\n")}\n\n`; | ||||
| } | ||||
|  | ||||
| /** | ||||
| @ -70,6 +70,13 @@ export function buildObserverPlugin({ serverURL, logPrefix, relativeRoot }) { | ||||
|         dispatcher.addEventListener("esbuild:error", listener); | ||||
|         dispatcher.addEventListener("esbuild:end", listener); | ||||
|  | ||||
|         const keepAliveInterval = setInterval(() => { | ||||
|             console.timeStamp("🏓 Keep-alive"); | ||||
|  | ||||
|             res.write("event: keep-alive\n\n"); | ||||
|             res.write(serializeCustomEventToStream(new CustomEvent("esbuild:keep-alive"))); | ||||
|         }, 15_000); | ||||
|  | ||||
|         req.on("close", () => { | ||||
|             console.log("🔌 Client disconnected"); | ||||
|  | ||||
| @ -79,13 +86,6 @@ export function buildObserverPlugin({ serverURL, logPrefix, relativeRoot }) { | ||||
|             dispatcher.removeEventListener("esbuild:error", listener); | ||||
|             dispatcher.removeEventListener("esbuild:end", listener); | ||||
|         }); | ||||
|  | ||||
|         const keepAliveInterval = setInterval(() => { | ||||
|             console.timeStamp("🏓 Keep-alive"); | ||||
|  | ||||
|             res.write("event: keep-alive\n\n"); | ||||
|             res.write(serializeCustomEventToStream(new CustomEvent("esbuild:keep-alive"))); | ||||
|         }, 15_000); | ||||
|     }); | ||||
|  | ||||
|     return { | ||||
|  | ||||
| @ -1,56 +0,0 @@ | ||||
| import { execFileSync } from "child_process"; | ||||
| import { ESLint } from "eslint"; | ||||
| import fs from "fs"; | ||||
| import path from "path"; | ||||
| import process from "process"; | ||||
| import { fileURLToPath } from "url"; | ||||
|  | ||||
| function changedFiles() { | ||||
|     const gitStatus = execFileSync("git", ["diff", "--name-only", "HEAD"], { encoding: "utf8" }); | ||||
|     const gitUntracked = execFileSync("git", ["ls-files", "--others", "--exclude-standard"], { | ||||
|         encoding: "utf8", | ||||
|     }); | ||||
|  | ||||
|     const changed = gitStatus | ||||
|         .split("\n") | ||||
|         .filter((line) => line.trim().substring(0, 4) === "web/") | ||||
|         .filter((line) => /\.(m|c)?(t|j)s$/.test(line)) | ||||
|         .map((line) => line.substring(4)) | ||||
|         .filter((line) => fs.existsSync(line)); | ||||
|  | ||||
|     const untracked = gitUntracked | ||||
|         .split("\n") | ||||
|         .filter((line) => /\.(m|c)?(t|j)s$/.test(line)) | ||||
|         .filter((line) => fs.existsSync(line)); | ||||
|  | ||||
|     const sourceFiles = [...changed, ...untracked].filter((line) => /^src\//.test(line)); | ||||
|     const scriptFiles = [...changed, ...untracked].filter( | ||||
|         (line) => /^scripts\//.test(line) || !/^src\//.test(line), | ||||
|     ); | ||||
|  | ||||
|     return [...sourceFiles, ...scriptFiles]; | ||||
| } | ||||
|  | ||||
| const __dirname = fileURLToPath(new URL(".", import.meta.url)); | ||||
| const projectRoot = path.join(__dirname, ".."); | ||||
| process.chdir(projectRoot); | ||||
|  | ||||
| const hasFlag = (flags) => process.argv.length > 1 && flags.includes(process.argv[2]); | ||||
|  | ||||
| const [configFile, files] = hasFlag(["-n", "--nightmare"]) | ||||
|     ? [path.join(__dirname, "eslint.nightmare.mjs"), changedFiles()] | ||||
|     : hasFlag(["-p", "--precommit"]) | ||||
|       ? [path.join(__dirname, "eslint.precommit.mjs"), changedFiles()] | ||||
|       : [path.join(projectRoot, "eslint.config.mjs"), ["."]]; | ||||
|  | ||||
| const eslint = new ESLint({ | ||||
|     overrideConfigFile: configFile, | ||||
|     warnIgnored: false, | ||||
| }); | ||||
|  | ||||
| const results = await eslint.lintFiles(files); | ||||
| const formatter = await eslint.loadFormatter("stylish"); | ||||
| const resultText = formatter.format(results); | ||||
| const errors = results.reduce((acc, result) => acc + result.errorCount, 0); | ||||
| console.log(resultText); | ||||
| process.exit(errors > 1 ? 1 : 0); | ||||
| @ -1,217 +0,0 @@ | ||||
| import eslint from "@eslint/js"; | ||||
| import tsparser from "@typescript-eslint/parser"; | ||||
| import litconf from "eslint-plugin-lit"; | ||||
| import wcconf from "eslint-plugin-wc"; | ||||
| import globals from "globals"; | ||||
| import tseslint from "typescript-eslint"; | ||||
|  | ||||
| const MAX_DEPTH = 4; | ||||
| const MAX_NESTED_CALLBACKS = 4; | ||||
| const MAX_PARAMS = 5; | ||||
|  | ||||
| // Waiting for SonarJS to be compatible | ||||
| // const MAX_COGNITIVE_COMPLEXITY = 9; | ||||
|  | ||||
| const rules = { | ||||
|     "accessor-pairs": "error", | ||||
|     "array-callback-return": "error", | ||||
|     "block-scoped-var": "error", | ||||
|     "consistent-return": "error", | ||||
|     "consistent-this": ["error", "that"], | ||||
|     "curly": ["error", "all"], | ||||
|     "dot-notation": [ | ||||
|         "error", | ||||
|         { | ||||
|             allowKeywords: true, | ||||
|         }, | ||||
|     ], | ||||
|     "eqeqeq": "error", | ||||
|     "func-names": "error", | ||||
|     "guard-for-in": "error", | ||||
|     "max-depth": ["error", MAX_DEPTH], | ||||
|     "max-nested-callbacks": ["error", MAX_NESTED_CALLBACKS], | ||||
|     "max-params": ["error", MAX_PARAMS], | ||||
|     "new-cap": "error", | ||||
|     "no-alert": "error", | ||||
|     "no-array-constructor": "error", | ||||
|     "no-bitwise": "error", | ||||
|     "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", | ||||
|     "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-invalid-this": "error", | ||||
|     "no-label-var": "error", | ||||
|     "no-lone-blocks": "error", | ||||
|     "no-lonely-if": "error", | ||||
|     "no-loop-func": "error", | ||||
|     "no-magic-numbers": ["error", { ignore: [0, 1, -1] }], | ||||
|     "no-multi-str": "error", | ||||
|     "no-negated-condition": "error", | ||||
|     "no-nested-ternary": "error", | ||||
|     "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", | ||||
|     "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", | ||||
|     "no-shadow": "error", | ||||
|     "no-shadow-restricted-names": "error", | ||||
|     "no-sparse-arrays": "error", | ||||
|     "no-this-before-super": "error", | ||||
|     "no-throw-literal": "error", | ||||
|     "no-trailing-spaces": "error", | ||||
|     "no-undef": "error", | ||||
|     "no-undef-init": "error", | ||||
|     "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-unused-vars": "off", | ||||
|     "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", | ||||
|     "@typescript-eslint/ban-ts-comment": "off", | ||||
|     "@typescript-eslint/no-unused-vars": [ | ||||
|         "error", | ||||
|         { | ||||
|             argsIgnorePattern: "^_", | ||||
|             varsIgnorePattern: "^_", | ||||
|             caughtErrorsIgnorePattern: "^_", | ||||
|         }, | ||||
|     ], | ||||
| }; | ||||
|  | ||||
| export default [ | ||||
|     // You would not believe how much this change has frustrated users: ["if an ignores key is used | ||||
|     // without any other keys in the configuration object, then the patterns act as global | ||||
|     // ignores"](https://eslint.org/docs/latest/use/configure/ignore) | ||||
|     { | ||||
|         ignores: [ | ||||
|             "dist/", | ||||
|             ".wireit/", | ||||
|             "packages/", | ||||
|             // don't ever lint node_modules | ||||
|             "node_modules/", | ||||
|             ".storybook/*", | ||||
|             // don't lint build output (make sure it's set to your correct build folder name) | ||||
|             // don't lint nyc coverage output | ||||
|             "coverage/", | ||||
|             "src/locale-codes.ts", | ||||
|             "storybook-static/", | ||||
|             "src/locales/", | ||||
|             "src/**/*.test.ts", | ||||
|         ], | ||||
|     }, | ||||
|     eslint.configs.recommended, | ||||
|     wcconf.configs["flat/recommended"], | ||||
|     litconf.configs["flat/recommended"], | ||||
|     ...tseslint.configs.recommended, | ||||
|     //     sonar.configs.recommended, | ||||
|     { | ||||
|         languageOptions: { | ||||
|             parser: tsparser, | ||||
|             parserOptions: { | ||||
|                 ecmaVersion: 12, | ||||
|                 sourceType: "module", | ||||
|             }, | ||||
|             globals: { | ||||
|                 ...globals.browser, | ||||
|             }, | ||||
|         }, | ||||
|         files: ["src/**"], | ||||
|         rules, | ||||
|     }, | ||||
|     { | ||||
|         languageOptions: { | ||||
|             parser: tsparser, | ||||
|             parserOptions: { | ||||
|                 ecmaVersion: 12, | ||||
|                 sourceType: "module", | ||||
|             }, | ||||
|             globals: { | ||||
|                 ...globals.nodeBuiltin, | ||||
|             }, | ||||
|         }, | ||||
|         files: ["scripts/*.mjs", "*.ts", "*.mjs"], | ||||
|         rules, | ||||
|     }, | ||||
|     { | ||||
|         languageOptions: { | ||||
|             parser: tsparser, | ||||
|             parserOptions: { | ||||
|                 ecmaVersion: 12, | ||||
|                 sourceType: "module", | ||||
|             }, | ||||
|             globals: { | ||||
|                 ...globals.nodeBuiltin, | ||||
|                 ...globals.jest, | ||||
|             }, | ||||
|         }, | ||||
|         files: ["src/**/*.test.ts"], | ||||
|         rules, | ||||
|     }, | ||||
| ]; | ||||
| @ -1,87 +0,0 @@ | ||||
| import eslint from "@eslint/js"; | ||||
| import tsparser from "@typescript-eslint/parser"; | ||||
| import litconf from "eslint-plugin-lit"; | ||||
| import wcconf from "eslint-plugin-wc"; | ||||
| import globals from "globals"; | ||||
| import tseslint from "typescript-eslint"; | ||||
|  | ||||
| export default [ | ||||
|     // You would not believe how much this change has frustrated users: ["if an ignores key is used | ||||
|     // without any other keys in the configuration object, then the patterns act as global | ||||
|     // ignores"](https://eslint.org/docs/latest/use/configure/ignore) | ||||
|     { | ||||
|         ignores: [ | ||||
|             "dist/", | ||||
|             ".wireit/", | ||||
|             "packages/", | ||||
|             // don't ever lint node_modules | ||||
|             "node_modules/", | ||||
|             ".storybook/*", | ||||
|             // don't lint build output (make sure it's set to your correct build folder name) | ||||
|             // don't lint nyc coverage output | ||||
|             "coverage/", | ||||
|             "src/locale-codes.ts", | ||||
|             "storybook-static/", | ||||
|             "src/locales/", | ||||
|         ], | ||||
|     }, | ||||
|     eslint.configs.recommended, | ||||
|     wcconf.configs["flat/recommended"], | ||||
|     litconf.configs["flat/recommended"], | ||||
|     ...tseslint.configs.recommended, | ||||
|     //    sonar.configs.recommended, | ||||
|     { | ||||
|         languageOptions: { | ||||
|             parser: tsparser, | ||||
|             parserOptions: { | ||||
|                 ecmaVersion: 12, | ||||
|                 sourceType: "module", | ||||
|             }, | ||||
|         }, | ||||
|         files: ["src/**"], | ||||
|         rules: { | ||||
|             "no-unused-vars": "off", | ||||
|             "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", 9], | ||||
|             //    "sonarjs/no-duplicate-string": "off", | ||||
|             //    "sonarjs/no-nested-template-literals": "off", | ||||
|             "@typescript-eslint/ban-ts-comment": "off", | ||||
|             "@typescript-eslint/no-unused-vars": [ | ||||
|                 "error", | ||||
|                 { | ||||
|                     argsIgnorePattern: "^_", | ||||
|                     varsIgnorePattern: "^_", | ||||
|                     caughtErrorsIgnorePattern: "^_", | ||||
|                 }, | ||||
|             ], | ||||
|         }, | ||||
|     }, | ||||
|     { | ||||
|         languageOptions: { | ||||
|             parser: tsparser, | ||||
|             parserOptions: { | ||||
|                 ecmaVersion: 12, | ||||
|                 sourceType: "module", | ||||
|             }, | ||||
|             globals: { | ||||
|                 ...globals.nodeBuiltin, | ||||
|             }, | ||||
|         }, | ||||
|         files: ["scripts/*.mjs", "*.ts", "*.mjs"], | ||||
|         rules: { | ||||
|             "no-unused-vars": "off", | ||||
|             "no-console": "off", | ||||
|             "@typescript-eslint/ban-ts-comment": "off", | ||||
|             "@typescript-eslint/no-unused-vars": [ | ||||
|                 "error", | ||||
|                 { | ||||
|                     argsIgnorePattern: "^_", | ||||
|                     varsIgnorePattern: "^_", | ||||
|                     caughtErrorsIgnorePattern: "^_", | ||||
|                 }, | ||||
|             ], | ||||
|         }, | ||||
|     }, | ||||
| ]; | ||||
| @ -1,22 +1,47 @@ | ||||
| import { readFileSync } from "fs"; | ||||
| import path from "path"; | ||||
| /** | ||||
|  * @import { Config } from "@lit/localize-tools/lib/types/config.js" | ||||
|  * @import { TransformOutputConfig } from "@lit/localize-tools/lib/types/modes.js" | ||||
|  * @import { Locale } from "@lit/localize-tools/lib/types/locale.js" | ||||
|  * @import { ProgramMessage } from "@lit/localize-tools/lib/messages.js" | ||||
|  * @import { Message } from "@lit/localize-tools/lib/messages.js" | ||||
|  * | ||||
|  * @typedef {Config & { output: TransformOutputConfig; }} TransformerConfig | ||||
|  */ | ||||
| import * as path from "node:path"; | ||||
| import { fileURLToPath } from "node:url"; | ||||
| import pseudolocale from "pseudolocale"; | ||||
| import { fileURLToPath } from "url"; | ||||
|  | ||||
| import { makeFormatter } from "@lit/localize-tools/lib/formatters/index.js"; | ||||
| import { sortProgramMessages } from "@lit/localize-tools/lib/messages.js"; | ||||
| import { TransformLitLocalizer } from "@lit/localize-tools/lib/modes/transform.js"; | ||||
|  | ||||
| const __dirname = fileURLToPath(new URL(".", import.meta.url)); | ||||
| const pseudoLocale = "pseudo-LOCALE"; | ||||
| const pseudoLocale = /** @type {Locale} */ ("pseudo-LOCALE"); | ||||
|  | ||||
| const targetLocales = [pseudoLocale]; | ||||
| const baseConfig = JSON.parse(readFileSync(path.join(__dirname, "../lit-localize.json"), "utf-8")); | ||||
|  | ||||
| const baseConfig = await import("../lit-localize.json", { | ||||
|     with: { | ||||
|         type: "json", | ||||
|     }, | ||||
| }) | ||||
|     .then((module) => { | ||||
|         return /** @type {Config} */ (module.default); | ||||
|     }) | ||||
|  | ||||
|     .catch((error) => { | ||||
|         console.error("Failed to load lit-localize.json", error); | ||||
|         process.exit(1); | ||||
|     }); | ||||
|  | ||||
| // Need to make some internal specifications to satisfy the transformer. It doesn't actually matter | ||||
| // which Localizer we use (transformer or runtime), because all of the functionality we care about | ||||
| // is in their common parent class, but I had to pick one.  Everything else here is just pure | ||||
| // exploitation of the lit/localize-tools internals. | ||||
|  | ||||
| /** | ||||
|  * @type {TransformerConfig} | ||||
|  */ | ||||
| const config = { | ||||
|     ...baseConfig, | ||||
|     baseDir: path.join(__dirname, ".."), | ||||
| @ -25,18 +50,26 @@ const config = { | ||||
|         ...baseConfig.output, | ||||
|         mode: "transform", | ||||
|     }, | ||||
|     resolve: (path) => path, | ||||
|     resolve: (filePath) => filePath, | ||||
| }; | ||||
|  | ||||
| const pseudoMessagify = (message) => ({ | ||||
|     name: message.name, | ||||
|     contents: message.contents.map((content) => | ||||
|         typeof content === "string" ? pseudolocale(content, { prepend: "", append: "" }) : content, | ||||
|     ), | ||||
| }); | ||||
| /** | ||||
|  * @param {ProgramMessage} message | ||||
|  * @returns {Message} | ||||
|  */ | ||||
| function pseudoMessagify({ name, contents }) { | ||||
|     return { | ||||
|         name, | ||||
|         contents: contents.map((content) => | ||||
|             typeof content === "string" | ||||
|                 ? pseudolocale(content, { prepend: "", append: "" }) | ||||
|                 : content, | ||||
|         ), | ||||
|     }; | ||||
| } | ||||
|  | ||||
| const localizer = new TransformLitLocalizer(config); | ||||
| const messages = localizer.extractSourceMessages().messages; | ||||
| const { messages } = localizer.extractSourceMessages(); | ||||
| const translations = messages.map(pseudoMessagify); | ||||
| const sorted = sortProgramMessages([...messages]); | ||||
| const formatter = makeFormatter(config); | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Teffen Ellis
					Teffen Ellis