@r_masseater/msch 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/{cli-contract-Wo9XK0jk.mjs → cli-contract-BRpk_UKr.mjs} +15 -7
- package/dist/cli-contract-BRpk_UKr.mjs.map +1 -0
- package/dist/cli.mjs +116 -5
- package/dist/cli.mjs.map +1 -1
- package/dist/lint.mjs +19 -1
- package/dist/lint.mjs.map +1 -1
- package/dist/oxlint.mjs +1 -1
- package/package.json +1 -1
- package/dist/cli-contract-Wo9XK0jk.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -38,5 +38,7 @@ oxlint のバージョンは、@effect/tsgo が対応するものに合わせる
|
|
|
38
38
|
|
|
39
39
|
## msch の検査
|
|
40
40
|
|
|
41
|
-
リポジトリのルートで `msch` を実行すると、値の集合と形の重複、overrides
|
|
41
|
+
リポジトリのルートで `msch` を実行すると、値の集合と形の重複、overrides の要不要、テストのためだけのコード、見えない文字、Python の使用を調べる。
|
|
42
|
+
見えない文字は、ゼロ幅の文字・書字方向を変える制御文字・タグ文字のように、エディタやレビューで見えずに意味を変えうる文字で、`git ls-files` に載るテキストのファイルから探す。ファイル先頭の BOM と、NUL を含むバイナリは対象にしない。
|
|
43
|
+
Python は使わない方針なので、`.py`・`pyproject.toml`・`uv.lock` などのファイルを指摘する。スクリプト・CI・設定の中で `python3`・`pip`・`uv`・`poetry` を呼ぶ箇所と、`actions/setup-python` などの action、`mise.toml` に書いた Python の道具も指摘する。
|
|
42
44
|
指摘があれば終了コードは 1 になる。`--json` を付けると結果を JSON で出す。
|
|
@@ -6,25 +6,30 @@ import { layer } from "@effect/platform-node/NodeServices";
|
|
|
6
6
|
import { ChildProcess, ChildProcessSpawner } from "effect/process";
|
|
7
7
|
// src/repository-files.ts
|
|
8
8
|
const GENERATED_PATHSPEC = ":(attr:linguist-generated)";
|
|
9
|
+
const LISTED_ARGS = [
|
|
10
|
+
"--cached",
|
|
11
|
+
"--others",
|
|
12
|
+
"--exclude-standard"
|
|
13
|
+
];
|
|
9
14
|
var RepositoryFiles = class extends Context.Service()("@r_masseater/msch/repository-files/RepositoryFiles") {};
|
|
10
15
|
const nodeRepositoryFiles = Layer.effect(RepositoryFiles, Effect.gen(function* nodeRepositoryFiles() {
|
|
11
16
|
const fs = yield* FileSystem.FileSystem;
|
|
12
17
|
const path = yield* Path.Path;
|
|
13
18
|
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
14
|
-
const
|
|
19
|
+
const gitFiles = (root, args) => Effect.gen(function* listFiles() {
|
|
15
20
|
const listed = yield* (yield* ChildProcess.make("git", [
|
|
16
21
|
"ls-files",
|
|
17
22
|
"-z",
|
|
18
|
-
|
|
19
|
-
GENERATED_PATHSPEC
|
|
23
|
+
...args
|
|
20
24
|
], {
|
|
21
25
|
cwd: root,
|
|
22
26
|
stdin: "ignore"
|
|
23
27
|
})).stdout.pipe(Stream.decodeText(), Stream.mkString);
|
|
24
|
-
return pipe(String$1.split(listed, "\0"), Array.filter(String$1.isNonEmpty)
|
|
28
|
+
return pipe(String$1.split(listed, "\0"), Array.filter(String$1.isNonEmpty));
|
|
25
29
|
}).pipe(Effect.scoped, Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), Effect.orElseSucceed(() => []));
|
|
26
30
|
return RepositoryFiles.of({
|
|
27
|
-
generatedFiles,
|
|
31
|
+
generatedFiles: (root) => gitFiles(root, ["--", GENERATED_PATHSPEC]).pipe(Effect.map(Array.map((file) => path.join(root, file)))),
|
|
32
|
+
listedFiles: (root) => gitFiles(root, LISTED_ARGS),
|
|
28
33
|
readFile: (file) => fs.readFileString(file).pipe(Effect.option),
|
|
29
34
|
exists: (file) => fs.exists(file).pipe(Effect.orElseSucceed(() => false)),
|
|
30
35
|
realPath: (file) => fs.realPath(file).pipe(Effect.orElseSucceed(() => file)),
|
|
@@ -230,7 +235,10 @@ const RuleNameSchema = Schema.Literals([
|
|
|
230
235
|
"library-value-set",
|
|
231
236
|
"library-shape",
|
|
232
237
|
"unused-override",
|
|
233
|
-
"removable-override"
|
|
238
|
+
"removable-override",
|
|
239
|
+
"hidden-unicode",
|
|
240
|
+
"python-file",
|
|
241
|
+
"python-command"
|
|
234
242
|
]);
|
|
235
243
|
const ProblemSchema = Schema.Struct({
|
|
236
244
|
file: Schema.String,
|
|
@@ -268,4 +276,4 @@ const formatProblem = (relative) => (problem) => `${relative(problem.file)}:${St
|
|
|
268
276
|
const JSON_FLAG = "--json";
|
|
269
277
|
export { isSignatureDeclaration as $, API as A, isCallExpression as B, MANIFEST_FILE as C, workspaceDependencies as D, readManifest as E, getLeadingCommentRanges as F, isInterfaceDeclaration as G, isElementAccessExpression as H, getNameOfDeclaration as I, isNewExpression as J, isModuleBlock as K, getTextOfJSDocComment as L, InternalSymbolName as M, SyntaxKind as N, RepositoryFiles as O, getJSDocTags as P, isPropertyAssignment as Q, isArrayLiteralExpression as R, uniqueByReference as S, dependencyEntries as T, isEnumDeclaration as U, isClassDeclaration as V, isIdentifier as W, isObjectLiteralExpression as X, isNumericLiteral as Y, isPropertyAccessExpression as Z, lineOf as _, problemAt as a, isUnionTypeNode as at, resolvedSymbol as b, childrenOf as c, isVariableStatement as ct, declarationsOf as d, isSourceFile as et, descendantsOf as f, isTypeDeclaration as g, isGenericDeclaration as h, formatProblem as i, isTypeReferenceNode as it, IndexKind as j, nodeRepositoryFiles as k, columnOf as l, isExternalDeclaration as m, ProblemList as n, isTypeAliasDeclaration as nt, sortProblems as o, isVariableDeclaration as ot, importedModulesOf as p, isModuleDeclaration as q, describeFinding as r, isTypeLiteralNode as rt, NODE_MODULES_SEGMENT as s, isVariableDeclarationList as st, JSON_FLAG as t, isStringLiteral as tt, contains as u, propertyNamesOf as v, NODE_MODULES as w, symbolAt as x, referencedSymbols as y, isBinaryExpression as z };
|
|
270
278
|
|
|
271
|
-
//# sourceMappingURL=cli-contract-
|
|
279
|
+
//# sourceMappingURL=cli-contract-BRpk_UKr.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-contract-BRpk_UKr.mjs","names":["Str","Arr","nodeServicesLayer","Rec","Arr","Str","Arr","Str","Arr"],"sources":["../src/repository-files.ts","../src/dependencies.ts","../src/syntax.ts","../src/problem.ts","../src/cli-contract.ts"],"sourcesContent":["import { layer as nodeServicesLayer } from \"@effect/platform-node/NodeServices\";\nimport {\n Array as Arr,\n Context,\n Effect,\n FileSystem,\n Layer,\n Path,\n Stream,\n String as Str,\n pipe,\n} from \"effect\";\nimport type { Option } from \"effect\";\nimport { ChildProcess, ChildProcessSpawner } from \"effect/process\";\n\nconst GENERATED_PATHSPEC = \":(attr:linguist-generated)\";\nconst LISTED_ARGS = [\"--cached\", \"--others\", \"--exclude-standard\"];\n\nclass RepositoryFiles extends Context.Service<\n RepositoryFiles,\n {\n readonly readFile: (file: string) => Effect.Effect<Option.Option<string>>;\n readonly exists: (file: string) => Effect.Effect<boolean>;\n readonly realPath: (file: string) => Effect.Effect<string>;\n readonly readDirectory: (directory: string) => Effect.Effect<readonly string[]>;\n readonly generatedFiles: (root: string) => Effect.Effect<readonly string[]>;\n readonly listedFiles: (root: string) => Effect.Effect<readonly string[]>;\n readonly isDirectory: (file: string) => Effect.Effect<Option.Option<boolean>>;\n readonly join: (...segments: readonly string[]) => string;\n readonly resolve: (...segments: readonly string[]) => string;\n readonly relative: (from: string, to: string) => string;\n readonly dirname: (file: string) => string;\n readonly basename: (file: string) => string;\n }\n>()(\"@r_masseater/msch/repository-files/RepositoryFiles\") {}\n\nconst nodeRepositoryFiles = Layer.effect(\n RepositoryFiles,\n Effect.gen(function* nodeRepositoryFiles() {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;\n const gitFiles = (root: string, args: readonly string[]): Effect.Effect<readonly string[]> =>\n Effect.gen(function* listFiles() {\n const handle = yield* ChildProcess.make(\"git\", [\"ls-files\", \"-z\", ...args], {\n cwd: root,\n stdin: \"ignore\",\n });\n const listed = yield* handle.stdout.pipe(Stream.decodeText(), Stream.mkString);\n return pipe(Str.split(listed, \"\\0\"), Arr.filter(Str.isNonEmpty));\n }).pipe(\n Effect.scoped,\n Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner),\n Effect.orElseSucceed((): readonly string[] => []),\n );\n return RepositoryFiles.of({\n generatedFiles: (root) =>\n gitFiles(root, [\"--\", GENERATED_PATHSPEC]).pipe(\n Effect.map(Arr.map((file) => path.join(root, file))),\n ),\n listedFiles: (root) => gitFiles(root, LISTED_ARGS),\n readFile: (file) => fs.readFileString(file).pipe(Effect.option),\n exists: (file) => fs.exists(file).pipe(Effect.orElseSucceed(() => false)),\n realPath: (file) => fs.realPath(file).pipe(Effect.orElseSucceed(() => file)),\n readDirectory: (directory) =>\n fs.readDirectory(directory).pipe(Effect.orElseSucceed((): readonly string[] => [])),\n isDirectory: (file) =>\n fs.stat(file).pipe(\n Effect.map((info) => info.type === \"Directory\"),\n Effect.option,\n ),\n join: (...segments) => path.join(...segments),\n resolve: (...segments) => path.resolve(...segments),\n relative: (from, to) => path.relative(from, to),\n dirname: (file) => path.dirname(file),\n basename: (file) => path.basename(file),\n });\n }),\n).pipe(Layer.provide(nodeServicesLayer));\n\nexport { RepositoryFiles, nodeRepositoryFiles };\n","import {\n Array as Arr,\n Effect,\n Option,\n Order,\n Predicate,\n Schema,\n String as Str,\n Record as Rec,\n pipe,\n} from \"effect\";\n\nimport { RepositoryFiles } from \"./repository-files.ts\";\n\ninterface DependencyEntry {\n readonly packageName: string;\n readonly specifier: string;\n readonly declarationsPath: string;\n}\n\nconst DependencyMap = Schema.optional(Schema.Record(Schema.String, Schema.String));\n\nconst PackageManifest = Schema.Struct({\n name: Schema.optional(Schema.String),\n dependencies: DependencyMap,\n devDependencies: DependencyMap,\n peerDependencies: DependencyMap,\n exports: Schema.optional(Schema.Unknown),\n types: Schema.optional(Schema.String),\n typings: Schema.optional(Schema.String),\n main: Schema.optional(Schema.String),\n});\n\ntype Manifest = typeof PackageManifest.Type;\n\nconst MANIFEST_FILE = \"package.json\";\nconst NODE_MODULES = \"node_modules\";\nconst WORKSPACE_PROTOCOL = \"workspace:\";\nconst ROOT_SUBPATH = \".\";\nconst SUBPATH_PREFIX = \"./\";\nconst WILDCARD = \"*\";\nconst TYPES_CONDITION = \"types\";\nconst TYPES_SCOPE = \"@types/\";\nconst TYPES_SCOPE_SEPARATOR = \"__\";\nconst CONDITION_DEPTH_LIMIT = 8;\nconst ROOT_DEPTH = 0;\nconst DEPTH_STEP = 1;\nconst DECLARATIONS_SUFFIX = /\\.d\\.[cm]?ts$|\\.[cm]?tsx?$/u;\nconst SCRIPT_SUFFIX = /\\.(?<module>[cm]?)js$/u;\n\nconst readManifest = Effect.fn(\"readManifest\")(function* readManifest(directory: string) {\n const files = yield* RepositoryFiles;\n const manifestText = yield* files.readFile(files.join(directory, MANIFEST_FILE));\n const text = yield* Effect.fromOption(manifestText);\n return yield* Schema.decodeEffect(Schema.fromJsonString(PackageManifest))(text);\n}, Effect.option);\n\nconst conditionsOf = (held: unknown): readonly (readonly [string, unknown])[] => {\n if (!Predicate.isObject(held)) {\n return [];\n }\n return Rec.toEntries(held);\n};\n\nconst typesConditionIn = (held: unknown, depth: number): Option.Option<string> => {\n if (depth > CONDITION_DEPTH_LIMIT) {\n return Option.none();\n }\n const entries = conditionsOf(held);\n const declared = Arr.findFirst(entries, ([condition, target]) =>\n Option.liftPredicate(\n target,\n (candidate) => condition === TYPES_CONDITION && Predicate.isString(candidate),\n ),\n ).pipe(Option.filter(Predicate.isString));\n return Option.orElse(declared, () =>\n Arr.findFirst(entries, ([, nested]) => typesConditionIn(nested, depth + DEPTH_STEP)),\n );\n};\n\nconst anyConditionIn = (held: unknown, depth: number): Option.Option<string> => {\n if (Predicate.isString(held)) {\n return Option.some(held);\n }\n if (depth > CONDITION_DEPTH_LIMIT) {\n return Option.none();\n }\n return Arr.findFirst(conditionsOf(held), ([, nested]) =>\n anyConditionIn(nested, depth + DEPTH_STEP),\n );\n};\n\nconst targetPathsOf = (target: unknown): readonly string[] =>\n Arr.getSomes([typesConditionIn(target, ROOT_DEPTH), anyConditionIn(target, ROOT_DEPTH)]);\n\ninterface SubpathTarget {\n readonly subpath: string;\n readonly target: unknown;\n}\n\nconst subpathTargetsOf = (exportsField: unknown): readonly SubpathTarget[] => {\n if (Predicate.isString(exportsField)) {\n return [{ subpath: ROOT_SUBPATH, target: exportsField }];\n }\n const entries = conditionsOf(exportsField);\n if (\n !pipe(\n entries,\n Arr.some(([subpath]) => Str.startsWith(ROOT_SUBPATH)(subpath)),\n )\n ) {\n return Arr.match(entries, {\n onEmpty: () => [],\n onNonEmpty: () => [{ subpath: ROOT_SUBPATH, target: exportsField }],\n });\n }\n return pipe(\n entries,\n Arr.filter(([subpath]) => subpath === ROOT_SUBPATH || Str.startsWith(SUBPATH_PREFIX)(subpath)),\n Arr.filter(([subpath]) => !Str.includes(WILDCARD)(subpath)),\n Arr.map(([subpath, target]) => ({ subpath, target })),\n );\n};\n\nconst declarationsCandidateFor = (entryPath: string): Option.Option<string> => {\n if (DECLARATIONS_SUFFIX.test(entryPath)) {\n return Option.some(entryPath);\n }\n if (!SCRIPT_SUFFIX.test(entryPath)) {\n return Option.none();\n }\n return Option.some(Str.replace(SCRIPT_SUFFIX, \".d.$<module>ts\")(entryPath));\n};\n\nconst firstDeclarationsAmong = Effect.fn(\"firstDeclarationsAmong\")(function* firstDeclarations(\n directory: string,\n entryPaths: readonly string[],\n) {\n const files = yield* RepositoryFiles;\n const candidates = Arr.getSomes(\n pipe(\n entryPaths,\n Arr.map((entryPath) => declarationsCandidateFor(entryPath)),\n ),\n );\n const existing = yield* Effect.filter(\n pipe(\n candidates,\n Arr.map((candidate) => files.join(directory, candidate)),\n ),\n (candidate) => files.exists(candidate),\n );\n return yield* Option.match(Arr.head(existing), {\n onNone: (): Effect.Effect<Option.Option<string>> => Effect.succeedNone,\n onSome: (candidate) => files.realPath(candidate).pipe(Effect.asSome),\n });\n});\n\nconst importNameOf = (packageName: string): string => {\n if (!Str.startsWith(TYPES_SCOPE)(packageName)) {\n return packageName;\n }\n const unscoped = Str.slice(TYPES_SCOPE.length)(packageName);\n if (!Str.includes(TYPES_SCOPE_SEPARATOR)(unscoped)) {\n return unscoped;\n }\n return `@${Str.replace(TYPES_SCOPE_SEPARATOR, \"/\")(unscoped)}`;\n};\n\nconst specifierFor = (packageName: string, subpath: string): string => {\n const importName = importNameOf(packageName);\n if (subpath === ROOT_SUBPATH) {\n return importName;\n }\n return `${importName}/${Str.slice(SUBPATH_PREFIX.length)(subpath)}`;\n};\n\nconst rootEntryPaths = (manifest: Manifest): readonly string[] =>\n Arr.getSomes([\n Option.fromUndefinedOr(manifest.types),\n Option.fromUndefinedOr(manifest.typings),\n Option.fromUndefinedOr(manifest.main),\n ]);\n\nconst subpathEntriesOf = Effect.fn(\"subpathEntriesOf\")(function* subpathEntriesOf(\n packageName: string,\n directory: string,\n manifest: Manifest,\n) {\n const found = yield* Effect.forEach(subpathTargetsOf(manifest.exports), ({ subpath, target }) =>\n firstDeclarationsAmong(directory, targetPathsOf(target)).pipe(\n Effect.map(\n Option.map((declarationsPath) => ({\n packageName,\n specifier: specifierFor(packageName, subpath),\n declarationsPath,\n })),\n ),\n ),\n );\n return Arr.getSomes(found);\n});\n\nconst entriesOfDependency = Effect.fn(\"entriesOfDependency\")(function* entriesOfDependency(\n packageName: string,\n directory: string,\n) {\n const manifest = yield* readManifest(directory);\n if (Option.isNone(manifest)) {\n return [];\n }\n const subpathEntries = yield* subpathEntriesOf(packageName, directory, manifest.value);\n const rootSpecifier = specifierFor(packageName, ROOT_SUBPATH);\n if (\n pipe(\n subpathEntries,\n Arr.some((entry) => entry.specifier === rootSpecifier),\n )\n ) {\n return subpathEntries;\n }\n const rootDeclarations = yield* firstDeclarationsAmong(directory, rootEntryPaths(manifest.value));\n const rootEntries = rootDeclarations.pipe(\n Option.map((declarationsPath) => ({ packageName, specifier: rootSpecifier, declarationsPath })),\n Option.toArray,\n );\n return [...rootEntries, ...subpathEntries];\n});\n\nconst declaredDependencies = (manifest: Manifest): readonly string[] =>\n pipe(\n Rec.toEntries({\n ...manifest.dependencies,\n ...manifest.devDependencies,\n ...manifest.peerDependencies,\n }),\n Arr.filter(([, range]) => !Str.startsWith(WORKSPACE_PROTOCOL)(range)),\n Arr.map(([packageName]) => packageName),\n );\n\nconst workspaceDependencies = (manifest: Manifest): readonly string[] =>\n pipe(\n Rec.toEntries({ ...manifest.dependencies, ...manifest.devDependencies }),\n Arr.filter(([, range]) => Str.startsWith(WORKSPACE_PROTOCOL)(range)),\n Arr.map(([packageName]) => packageName),\n );\n\nconst entryOrder = Order.mapInput(Order.String, (entry: DependencyEntry) => entry.specifier);\n\nconst dependencyEntries = Effect.fn(\"dependencyEntries\")(function* dependencyEntries(\n packageDirectory: string,\n) {\n const files = yield* RepositoryFiles;\n const manifest = yield* readManifest(packageDirectory);\n if (Option.isNone(manifest)) {\n return [];\n }\n const entries = yield* Effect.forEach(declaredDependencies(manifest.value), (packageName) =>\n entriesOfDependency(packageName, files.join(packageDirectory, NODE_MODULES, packageName)),\n );\n return Arr.sort(\n Arr.dedupeWith(entries.flat(), (left, right) => left.specifier === right.specifier),\n entryOrder,\n );\n});\n\nexport { MANIFEST_FILE, NODE_MODULES, dependencyEntries, readManifest, workspaceDependencies };\nexport type { DependencyEntry, Manifest };\n","import { Array as Arr, Option, String as Str, pipe } from \"effect\";\n\nimport {\n isEnumDeclaration,\n isExportDeclaration,\n isIdentifier,\n isImportDeclaration,\n isInterfaceDeclaration,\n isStringLiteral,\n isTypeAliasDeclaration,\n SyntaxKind,\n} from \"./compiler.ts\";\nimport type {\n Checker,\n Declaration,\n EnumDeclaration,\n InterfaceDeclaration,\n Node,\n Program,\n SourceFile,\n TypeSymbol,\n Type,\n TypeAliasDeclaration,\n} from \"./compiler.ts\";\nimport { NODE_MODULES } from \"./dependencies.ts\";\n\nconst NODE_MODULES_SEGMENT = `/${NODE_MODULES}/`;\n\nconst POSITION_BASE = 1;\n\nconst uniqueByReference = <Item>(items: readonly Item[]): readonly Item[] =>\n Arr.dedupeWith(items, (left, right) => left === right);\n\nconst childrenOf = (node: Node): readonly Node[] => {\n const children: Node[] = [];\n node.forEachChild((child) => {\n children.push(child);\n });\n return children;\n};\n\nconst descendantsOf = (node: Node): readonly Node[] =>\n pipe(\n childrenOf(node),\n Arr.flatMap((child) => Arr.prepend(descendantsOf(child), child)),\n );\n\nconst isExternalFile =\n (program: Program) =>\n (sourceFile: SourceFile): boolean =>\n Str.includes(NODE_MODULES_SEGMENT)(sourceFile.fileName) ||\n program.isSourceFileFromExternalLibrary(sourceFile);\n\nconst isExternalDeclaration =\n (program: Program) =>\n (declaration: Node): boolean =>\n isExternalFile(program)(declaration.getSourceFile());\n\nconst ALIAS_KINDS: ReadonlySet<SyntaxKind> = new Set([\n SyntaxKind.ImportSpecifier,\n SyntaxKind.ImportClause,\n SyntaxKind.NamespaceImport,\n SyntaxKind.ExportSpecifier,\n SyntaxKind.ImportEqualsDeclaration,\n SyntaxKind.ExportAssignment,\n]);\n\nconst declarationsOf = (symbol: TypeSymbol): readonly Declaration[] =>\n pipe(\n symbol.declarations,\n Arr.flatMap((handle) => Option.toArray(Option.fromUndefinedOr(handle.resolve()))),\n );\n\nconst isAliasDeclaration = (declaration: Node): boolean => ALIAS_KINDS.has(declaration.kind);\n\nconst resolvedSymbol =\n (checker: Checker) =>\n (symbol: TypeSymbol): TypeSymbol => {\n if (\n !pipe(\n declarationsOf(symbol),\n Arr.some((declaration) => isAliasDeclaration(declaration)),\n )\n ) {\n return symbol;\n }\n return checker.getAliasedSymbol(symbol);\n };\n\nconst symbolAt =\n (checker: Checker) =>\n (node: Node): Option.Option<TypeSymbol> =>\n Option.fromUndefinedOr(checker.getSymbolAtLocation(node)).pipe(\n Option.map(resolvedSymbol(checker)),\n );\n\nconst referencedSymbols =\n (checker: Checker) =>\n (node: Node): readonly TypeSymbol[] => {\n const identifiers = pipe(\n Arr.prepend(descendantsOf(node), node),\n Arr.filter((candidate) => isIdentifier(candidate)),\n );\n const symbols = pipe(\n checker.getSymbolAtLocation(identifiers),\n Arr.flatMap((symbol) => Option.toArray(Option.fromUndefinedOr(symbol))),\n Arr.map(resolvedSymbol(checker)),\n );\n return uniqueByReference(symbols);\n };\n\nconst lineOf = (node: Node): number => {\n const sourceFile = node.getSourceFile();\n return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + POSITION_BASE;\n};\n\nconst columnOf = (node: Node): number => {\n const sourceFile = node.getSourceFile();\n return (\n sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).character + POSITION_BASE\n );\n};\n\nconst contains =\n (outer: Node) =>\n (inner: Node): boolean =>\n outer.getSourceFile().fileName === inner.getSourceFile().fileName &&\n outer.getStart() <= inner.getStart() &&\n inner.getEnd() <= outer.getEnd();\n\nconst isTypeDeclaration = (\n declaration: Node,\n): declaration is TypeAliasDeclaration | InterfaceDeclaration | EnumDeclaration =>\n isTypeAliasDeclaration(declaration) ||\n isInterfaceDeclaration(declaration) ||\n isEnumDeclaration(declaration);\n\nconst isGenericDeclaration = (declaration: Node): boolean =>\n (isTypeAliasDeclaration(declaration) || isInterfaceDeclaration(declaration)) &&\n Arr.isReadonlyArrayNonEmpty(declaration.typeParameters ?? []);\n\nconst propertyNamesOf =\n (checker: Checker) =>\n (type: Type): readonly string[] =>\n pipe(\n checker.getPropertiesOfType(type),\n Arr.map((property) => property.name),\n );\n\nconst moduleSpecifierOf = (statement: Node): Option.Option<string> => {\n if (!isImportDeclaration(statement) && !isExportDeclaration(statement)) {\n return Option.none();\n }\n return Option.fromUndefinedOr(statement.moduleSpecifier).pipe(\n Option.filter((specifier) => isStringLiteral(specifier)),\n Option.map((specifier) => specifier.text),\n );\n};\n\nconst importedModulesOf = (sourceFile: SourceFile): readonly string[] =>\n pipe(\n sourceFile.statements,\n Arr.map((statement) => moduleSpecifierOf(statement)),\n Arr.getSomes,\n );\n\nexport {\n NODE_MODULES_SEGMENT,\n POSITION_BASE,\n childrenOf,\n columnOf,\n contains,\n declarationsOf,\n descendantsOf,\n importedModulesOf,\n isExternalDeclaration,\n isGenericDeclaration,\n isTypeDeclaration,\n lineOf,\n propertyNamesOf,\n referencedSymbols,\n resolvedSymbol,\n symbolAt,\n uniqueByReference,\n};\n","import { Array as Arr, Order, Schema, pipe } from \"effect\";\n\nimport type { Node } from \"./compiler.ts\";\nimport { columnOf, lineOf } from \"./syntax.ts\";\n\nconst RuleNameSchema = Schema.Literals([\n \"owner-annotation\",\n \"duplicate-value-set\",\n \"owner-value-set\",\n \"owner-extension\",\n \"owned-literal\",\n \"library-value-set\",\n \"library-shape\",\n \"unused-override\",\n \"removable-override\",\n \"hidden-unicode\",\n \"python-file\",\n \"python-command\",\n]);\n\ntype RuleName = typeof RuleNameSchema.Type;\n\nconst ProblemSchema = Schema.Struct({\n file: Schema.String,\n line: Schema.Int,\n column: Schema.Int,\n rule: RuleNameSchema,\n message: Schema.String,\n});\n\ntype Problem = typeof ProblemSchema.Type;\n\nconst ProblemList = Schema.Array(ProblemSchema);\n\nconst CANDIDATE_LIMIT = 3;\n\ninterface Finding {\n readonly rule: RuleName;\n readonly message: string;\n}\n\nconst problemAt =\n ({ rule, message }: Finding) =>\n (node: Node): Problem => ({\n file: node.getSourceFile().fileName,\n line: lineOf(node),\n column: columnOf(node),\n rule,\n message,\n });\n\nconst problemOrder: Order.Order<Problem> = Order.combineAll([\n Order.mapInput(Order.String, (problem: Problem) => problem.file),\n Order.mapInput(Order.Number, (problem: Problem) => problem.line),\n Order.mapInput(Order.Number, (problem: Problem) => problem.column),\n Order.mapInput(Order.String, (problem: Problem) => problem.rule),\n Order.mapInput(Order.String, (problem: Problem) => problem.message),\n]);\n\nconst problemKey = (problem: Problem): string =>\n pipe(\n [problem.file, String(problem.line), String(problem.column), problem.rule, problem.message],\n Arr.join(\"\\0\"),\n );\n\nconst sortProblems = (problems: readonly Problem[]): readonly Problem[] =>\n Arr.sort(\n Arr.dedupeWith(problems, (left, right) => problemKey(left) === problemKey(right)),\n problemOrder,\n );\n\nconst describeFinding = (problem: Problem): string => `[${problem.rule}] ${problem.message}`;\n\nconst formatProblem =\n (relative: (file: string) => string) =>\n (problem: Problem): string =>\n `${relative(problem.file)}:${String(problem.line)}:${String(problem.column)} ${describeFinding(problem)}`;\n\nexport { CANDIDATE_LIMIT, ProblemList, describeFinding, formatProblem, problemAt, sortProblems };\nexport type { Finding, Problem, RuleName };\n","const JSON_FLAG = \"--json\";\n\nexport { JSON_FLAG };\n"],"mappings":"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"}
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as isSignatureDeclaration, A as API, B as isCallExpression, C as MANIFEST_FILE, D as workspaceDependencies, E as readManifest, F as getLeadingCommentRanges, G as isInterfaceDeclaration, H as isElementAccessExpression, I as getNameOfDeclaration, J as isNewExpression, K as isModuleBlock, L as getTextOfJSDocComment, M as InternalSymbolName, N as SyntaxKind, O as RepositoryFiles, P as getJSDocTags, Q as isPropertyAssignment, R as isArrayLiteralExpression, S as uniqueByReference, T as dependencyEntries, U as isEnumDeclaration, V as isClassDeclaration, W as isIdentifier, X as isObjectLiteralExpression, Y as isNumericLiteral, Z as isPropertyAccessExpression, _ as lineOf, a as problemAt, at as isUnionTypeNode, b as resolvedSymbol, c as childrenOf, ct as isVariableStatement, d as declarationsOf, et as isSourceFile, f as descendantsOf, g as isTypeDeclaration, h as isGenericDeclaration, i as formatProblem, it as isTypeReferenceNode, j as IndexKind, k as nodeRepositoryFiles, l as columnOf, m as isExternalDeclaration, n as ProblemList, nt as isTypeAliasDeclaration, o as sortProblems, ot as isVariableDeclaration, p as importedModulesOf, q as isModuleDeclaration, rt as isTypeLiteralNode, s as NODE_MODULES_SEGMENT, st as isVariableDeclarationList, tt as isStringLiteral, u as contains, v as propertyNamesOf, w as NODE_MODULES, x as symbolAt, y as referencedSymbols, z as isBinaryExpression } from "./cli-contract-
|
|
2
|
+
import { $ as isSignatureDeclaration, A as API, B as isCallExpression, C as MANIFEST_FILE, D as workspaceDependencies, E as readManifest, F as getLeadingCommentRanges, G as isInterfaceDeclaration, H as isElementAccessExpression, I as getNameOfDeclaration, J as isNewExpression, K as isModuleBlock, L as getTextOfJSDocComment, M as InternalSymbolName, N as SyntaxKind, O as RepositoryFiles, P as getJSDocTags, Q as isPropertyAssignment, R as isArrayLiteralExpression, S as uniqueByReference, T as dependencyEntries, U as isEnumDeclaration, V as isClassDeclaration, W as isIdentifier, X as isObjectLiteralExpression, Y as isNumericLiteral, Z as isPropertyAccessExpression, _ as lineOf, a as problemAt, at as isUnionTypeNode, b as resolvedSymbol, c as childrenOf, ct as isVariableStatement, d as declarationsOf, et as isSourceFile, f as descendantsOf, g as isTypeDeclaration, h as isGenericDeclaration, i as formatProblem, it as isTypeReferenceNode, j as IndexKind, k as nodeRepositoryFiles, l as columnOf, m as isExternalDeclaration, n as ProblemList, nt as isTypeAliasDeclaration, o as sortProblems, ot as isVariableDeclaration, p as importedModulesOf, q as isModuleDeclaration, rt as isTypeLiteralNode, s as NODE_MODULES_SEGMENT, st as isVariableDeclarationList, tt as isStringLiteral, u as contains, v as propertyNamesOf, w as NODE_MODULES, x as symbolAt, y as referencedSymbols, z as isBinaryExpression } from "./cli-contract-BRpk_UKr.mjs";
|
|
3
3
|
import { Array, Cause, Effect, FileSystem, Function, Layer, Number as Number$1, Option, Order, Path, Record, Schema, Stdio, Stream, String as String$1, pipe } from "effect";
|
|
4
4
|
import { layer } from "@effect/platform-node/NodeServices";
|
|
5
5
|
import { ChildProcess } from "effect/process";
|
|
@@ -777,6 +777,54 @@ const checkRepository = Effect.fn("checkRepository")(function* checkRepository(r
|
|
|
777
777
|
...duplicateProblems(relative)(unowned)
|
|
778
778
|
]);
|
|
779
779
|
}, Effect.scoped);
|
|
780
|
+
// src/text-position.ts
|
|
781
|
+
const START$1 = 0;
|
|
782
|
+
const FIRST_COLUMN$1 = 1;
|
|
783
|
+
const positionOf = (source) => (index) => {
|
|
784
|
+
const before = pipe(source, String$1.slice(START$1, index), String$1.split("\n"));
|
|
785
|
+
return {
|
|
786
|
+
line: before.length,
|
|
787
|
+
column: pipe(Array.last(before), Option.match({
|
|
788
|
+
onNone: () => FIRST_COLUMN$1,
|
|
789
|
+
onSome: (last) => last.length + FIRST_COLUMN$1
|
|
790
|
+
}))
|
|
791
|
+
};
|
|
792
|
+
};
|
|
793
|
+
// src/hidden-unicode.ts
|
|
794
|
+
const HIDDEN = /[\u00AD\u180E\u200B-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u2069\uFEFF]|[\u{E0000}-\u{E007F}]/gu;
|
|
795
|
+
const NUL = "\0";
|
|
796
|
+
const START = 0;
|
|
797
|
+
const HEX = 16;
|
|
798
|
+
const CODE_POINT_WIDTH = 4;
|
|
799
|
+
const READ_CONCURRENCY$1 = 64;
|
|
800
|
+
const RULE = "hidden-unicode";
|
|
801
|
+
const hexOf = (character) => pipe(String$1.codePointAt(START)(character), Option.getOrElse(() => START), (code) => code.toString(HEX), String$1.toUpperCase, String$1.padStart(CODE_POINT_WIDTH, "0"));
|
|
802
|
+
const problemOf = (file, source) => (found) => {
|
|
803
|
+
const hex = hexOf(found[START]);
|
|
804
|
+
return {
|
|
805
|
+
file,
|
|
806
|
+
...positionOf(source)(found.index ?? START),
|
|
807
|
+
rule: RULE,
|
|
808
|
+
message: `U+${hex} is invisible in editors and reviews and can change what the text means. Remove it, or write it as the escape \\u${hex}.`
|
|
809
|
+
};
|
|
810
|
+
};
|
|
811
|
+
const problemsIn = (file, source) => {
|
|
812
|
+
if (String$1.includes(NUL)(source)) return [];
|
|
813
|
+
return pipe(Array.fromIterable(String$1.matchAll(HIDDEN)(source)), Array.map(problemOf(file, source)));
|
|
814
|
+
};
|
|
815
|
+
/** @internal */
|
|
816
|
+
const checkHiddenUnicode = Effect.fn("checkHiddenUnicode")(function* checkHiddenUnicode(root) {
|
|
817
|
+
const files = yield* RepositoryFiles;
|
|
818
|
+
const names = yield* files.listedFiles(root);
|
|
819
|
+
const found = yield* Effect.forEach(names, (name) => {
|
|
820
|
+
const file = files.join(root, name);
|
|
821
|
+
return files.readFile(file).pipe(Effect.map(Option.match({
|
|
822
|
+
onNone: () => [],
|
|
823
|
+
onSome: (source) => problemsIn(file, source)
|
|
824
|
+
})));
|
|
825
|
+
}, { concurrency: READ_CONCURRENCY$1 });
|
|
826
|
+
return Array.flatten(found);
|
|
827
|
+
});
|
|
780
828
|
// src/output.ts
|
|
781
829
|
const taggedError$1 = Schema.TaggedError;
|
|
782
830
|
var ProblemsFound = class extends taggedError$1()("ProblemsFound", { count: Schema.Finite }) {};
|
|
@@ -966,11 +1014,11 @@ const withBaselineProbe = (manifest) => {
|
|
|
966
1014
|
};
|
|
967
1015
|
// src/overrides.ts
|
|
968
1016
|
const WORKSPACE_FILE = "pnpm-workspace.yaml";
|
|
969
|
-
const LOCKFILE = "pnpm-lock.yaml";
|
|
1017
|
+
const LOCKFILE$1 = "pnpm-lock.yaml";
|
|
970
1018
|
const RESOLUTION_FILES = [
|
|
971
1019
|
"*package.json",
|
|
972
1020
|
WORKSPACE_FILE,
|
|
973
|
-
LOCKFILE,
|
|
1021
|
+
LOCKFILE$1,
|
|
974
1022
|
".npmrc",
|
|
975
1023
|
"patches/*"
|
|
976
1024
|
];
|
|
@@ -1042,7 +1090,7 @@ const resolutionWith = Effect.fn("resolutionWith")(function* resolutionWith(work
|
|
|
1042
1090
|
yield* copyFiles(workspace, directory);
|
|
1043
1091
|
yield* fs.writeFileString(path.join(directory, WORKSPACE_FILE), manifest);
|
|
1044
1092
|
const output = yield* resolveIn(directory, override);
|
|
1045
|
-
const lockfile = yield* fs.readFileString(path.join(directory, LOCKFILE));
|
|
1093
|
+
const lockfile = yield* fs.readFileString(path.join(directory, LOCKFILE$1));
|
|
1046
1094
|
yield* Effect.annotateCurrentSpan({ "msch.override": override });
|
|
1047
1095
|
const advisories = yield* auditIn(directory);
|
|
1048
1096
|
return resolutionOf({
|
|
@@ -1079,6 +1127,64 @@ const checkOverrides = Effect.fn("checkOverrides")(function* checkOverrides(root
|
|
|
1079
1127
|
if (Option.isNone(manifest) || !Array.isReadonlyArrayNonEmpty(keys)) return [];
|
|
1080
1128
|
return yield* problemsFor(root, manifest.value, keys);
|
|
1081
1129
|
});
|
|
1130
|
+
// src/python.ts
|
|
1131
|
+
const PYTHON_FILE = /(?:\.(?:py|pyi|pyw|pyx|ipynb)|(?:^|\/)(?:pyproject\.toml|uv\.lock|uv\.toml|poetry\.lock|Pipfile|Pipfile\.lock|setup\.cfg|tox\.ini|pytest\.ini|\.python-version|requirements[^/]*\.txt))$/u;
|
|
1132
|
+
const SCANNED_FILE = /(?:\.(?:[cm]?[jt]sx?|jsonc?|toml|ya?ml|sh|bash|zsh|fish|rs|cs|go|mk)|(?:^|\/)(?:Dockerfile[^/]*|Makefile|justfile|\.tool-versions|[^./][^./]*))$/u;
|
|
1133
|
+
const LOCKFILE = /(?:^|\/)(?:[^/]*\.lock|[^/]*-lock\.(?:json|ya?ml))$/u;
|
|
1134
|
+
const TOOL_VERSIONS = /(?:^|\/)(?:\.?mise(?:\.[^/]+)?\.toml|\.tool-versions)$/u;
|
|
1135
|
+
const COMMAND_PATTERNS = [
|
|
1136
|
+
/(?<![\w./@-])python[\d.]*[ \t]+(?:[-"'$./]|\S+\.py\b)/gu,
|
|
1137
|
+
/(?<![\w./@-])pip3?[ \t]+(?:install|uninstall|download|freeze|wheel|compile|sync)\b/gu,
|
|
1138
|
+
/(?<![\w./@-])pipx[ \t]+(?:run|install|inject|upgrade)\b/gu,
|
|
1139
|
+
/(?<![\w./@-])uvx[ \t]+[^\s=:,;)|&]/gu,
|
|
1140
|
+
/(?<![\w./@-])uv[ \t]+(?:run|sync|lock|pip|tool|venv|add|remove|python|export|build|publish|init|tree|cache|self)\b/gu,
|
|
1141
|
+
/(?<![\w./@-])poetry[ \t]+(?:run|install|add|lock|build|publish|shell|export)\b/gu,
|
|
1142
|
+
/(?:\bcommand["']?\s*[:=]\s*|(?:\bspawn\w*|\bexec\w*|\bexeca|Command::new|ChildProcess\.make|\$)\(\s*)["'`](?:python[\d.]*|pip3?|pipx|uvx?|poetry)["'`]/gu,
|
|
1143
|
+
/\buses:\s*["']?(?:actions\/setup-python|astral-sh\/setup-uv|astral-sh\/ruff-action|pypa\/[\w.-]+|snok\/install-poetry|abatilo\/actions-poetry|conda-incubator\/setup-miniconda|mamba-org\/setup-micromamba|pdm-project\/setup-pdm)\b/gu
|
|
1144
|
+
];
|
|
1145
|
+
const TOOL_PATTERNS = [/^[ \t]*["']?(?:python|uv|pipx|poetry|ruff|pdm|hatch|pipx:[^"'\s=]+)["']?[ \t]*(?:=|[ \t]+\S)/gmu];
|
|
1146
|
+
const READ_CONCURRENCY = 64;
|
|
1147
|
+
const FIRST_LINE = 1;
|
|
1148
|
+
const FIRST_COLUMN = 1;
|
|
1149
|
+
const WHOLE_MATCH = 0;
|
|
1150
|
+
const FILE_RULE = "python-file";
|
|
1151
|
+
const COMMAND_RULE = "python-command";
|
|
1152
|
+
const fileProblem = ({ file }) => ({
|
|
1153
|
+
file,
|
|
1154
|
+
line: FIRST_LINE,
|
|
1155
|
+
column: FIRST_COLUMN,
|
|
1156
|
+
rule: FILE_RULE,
|
|
1157
|
+
message: "Python is not used in this repository. Replace this file with TypeScript or Rust."
|
|
1158
|
+
});
|
|
1159
|
+
const patternsFor = (name) => {
|
|
1160
|
+
if (TOOL_VERSIONS.test(name)) return [...COMMAND_PATTERNS, ...TOOL_PATTERNS];
|
|
1161
|
+
return COMMAND_PATTERNS;
|
|
1162
|
+
};
|
|
1163
|
+
const commandProblem = ({ file }, source) => (found) => ({
|
|
1164
|
+
file,
|
|
1165
|
+
...positionOf(source)(found.index ?? WHOLE_MATCH),
|
|
1166
|
+
rule: COMMAND_RULE,
|
|
1167
|
+
message: `Python is not used in this repository. Remove \`${String$1.trim(found[WHOLE_MATCH])}\` and do the work in TypeScript or Rust.`
|
|
1168
|
+
});
|
|
1169
|
+
const commandProblems = (entry, source) => pipe(patternsFor(entry.name), Array.flatMap((pattern) => Array.fromIterable(String$1.matchAll(pattern)(source))), Array.map(commandProblem(entry, source)));
|
|
1170
|
+
const isPython = ({ name }) => PYTHON_FILE.test(name);
|
|
1171
|
+
const isScanned = (entry) => !isPython(entry) && SCANNED_FILE.test(entry.name) && !LOCKFILE.test(entry.name);
|
|
1172
|
+
/** @internal */
|
|
1173
|
+
const checkPython = Effect.fn("checkPython")(function* checkPython(root) {
|
|
1174
|
+
const files = yield* RepositoryFiles;
|
|
1175
|
+
const names = yield* files.listedFiles(root);
|
|
1176
|
+
const listed = pipe(names, Array.map((name) => ({
|
|
1177
|
+
name,
|
|
1178
|
+
file: files.join(root, name)
|
|
1179
|
+
})));
|
|
1180
|
+
const problemsIn = (entry) => files.readFile(entry.file).pipe(Effect.map(Option.match({
|
|
1181
|
+
onNone: () => [],
|
|
1182
|
+
onSome: (source) => commandProblems(entry, source)
|
|
1183
|
+
})));
|
|
1184
|
+
const scanned = yield* Effect.forEach(pipe(listed, Array.filter(isScanned)), problemsIn, { concurrency: READ_CONCURRENCY });
|
|
1185
|
+
const present = yield* Effect.filter(pipe(listed, Array.filter(isPython)), ({ file }) => files.exists(file));
|
|
1186
|
+
return [...pipe(present, Array.map(fileProblem)), ...Array.flatten(scanned)];
|
|
1187
|
+
});
|
|
1082
1188
|
// src/runtime.ts
|
|
1083
1189
|
const reportFailure = (cause) => Effect.gen(function* report() {
|
|
1084
1190
|
const stdio = yield* Stdio.Stdio;
|
|
@@ -1105,7 +1211,12 @@ runCli(Effect.gen(function* msch() {
|
|
|
1105
1211
|
const files = yield* RepositoryFiles;
|
|
1106
1212
|
const args = yield* stdio.args;
|
|
1107
1213
|
const root = files.resolve(".");
|
|
1108
|
-
const found = yield* Effect.all([
|
|
1214
|
+
const found = yield* Effect.all([
|
|
1215
|
+
checkRepository(root),
|
|
1216
|
+
checkOverrides(root),
|
|
1217
|
+
checkHiddenUnicode(root),
|
|
1218
|
+
checkPython(root)
|
|
1219
|
+
], { concurrency: "unbounded" });
|
|
1109
1220
|
const problems = sortProblems(Array.flatten(found));
|
|
1110
1221
|
if (args.includes("--json")) {
|
|
1111
1222
|
const text = yield* Schema.encodeEffect(Schema.fromJsonString(ProblemList))(problems);
|