@descent-vtt/spec-brief 0.9.0 → 0.11.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/CHANGELOG.md +86 -0
- package/README.md +31 -5
- package/dist/apply.d.ts +3 -2
- package/dist/apply.js +12 -3
- package/dist/apply.js.map +1 -1
- package/dist/cli.d.ts +10 -0
- package/dist/cli.js +74 -16
- package/dist/cli.js.map +1 -1
- package/dist/engine.d.ts +43 -0
- package/dist/engine.js +82 -13
- package/dist/engine.js.map +1 -1
- package/dist/fs.d.ts +40 -4
- package/dist/fs.js +59 -9
- package/dist/fs.js.map +1 -1
- package/dist/git.d.ts +11 -0
- package/dist/git.js +32 -2
- package/dist/git.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/plugins.js +19 -4
- package/dist/plugins.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -9,8 +9,8 @@ export { main, type CliIO, EXIT_ERROR, EXIT_FAILED, EXIT_OK, HELP, UsageError }
|
|
|
9
9
|
export { type Collision, collisionFindings, type CollisionOptions, type CollisionReport, collisions, type Overlap, type SharedDirectory, type UndecidedPair, type WaveMatrix, } from './collisions.js';
|
|
10
10
|
export { BANNER_PLACEHOLDERS, type Config, CONFIG_FILES, CONFIG_SCHEMA, ConfigError, configJsonSchema, DEFAULT_CONFIG, parseConfig, type PluginReference, resolveConfig, type SectionRule, } from './config.js';
|
|
11
11
|
export { buildCorpus, type Corpus, dependencyCycles, findBriefs, isReady, pendingDependencies, resolveDependency, type SourceFile, type Waiting, waitingOnDeferred, } from './corpus.js';
|
|
12
|
-
export { type ArchiveOptions, BriefEngine, EngineError, type EngineSetup, type NewOptions, type OpenOptions, today } from './engine.js';
|
|
13
|
-
export { type FileSystem, MemoryFileSystem, NodeFileSystem } from './fs.js';
|
|
12
|
+
export { type ArchiveOptions, BriefEngine, EngineError, type EngineSetup, type Inputs, type NewOptions, type OpenOptions, today } from './engine.js';
|
|
13
|
+
export { type FileSystem, MemoryFileSystem, NodeFileSystem, type NotAFile, NotAFileError, type Reads } from './fs.js';
|
|
14
14
|
export { type CommitInfo, type FileChange, type Git, GitError, NodeGit } from './git.js';
|
|
15
15
|
export { type Glob, globBase, globBases, globCovers, globWitness, intersectGlobs, type Literal, type LiteralReading, matchGlob, parseGlob, type ParseOptions, readingIn, WITNESS_BUDGET, type Witness, } from './glob.js';
|
|
16
16
|
export { integrityOf } from './integrity.js';
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ export { collisionFindings, collisions, } from './collisions.js';
|
|
|
10
10
|
export { BANNER_PLACEHOLDERS, CONFIG_FILES, CONFIG_SCHEMA, ConfigError, configJsonSchema, DEFAULT_CONFIG, parseConfig, resolveConfig, } from './config.js';
|
|
11
11
|
export { buildCorpus, dependencyCycles, findBriefs, isReady, pendingDependencies, resolveDependency, waitingOnDeferred, } from './corpus.js';
|
|
12
12
|
export { BriefEngine, EngineError, today } from './engine.js';
|
|
13
|
-
export { MemoryFileSystem, NodeFileSystem } from './fs.js';
|
|
13
|
+
export { MemoryFileSystem, NodeFileSystem, NotAFileError } from './fs.js';
|
|
14
14
|
export { GitError, NodeGit } from './git.js';
|
|
15
15
|
export { globBase, globBases, globCovers, globWitness, intersectGlobs, matchGlob, parseGlob, readingIn, WITNESS_BUDGET, } from './glob.js';
|
|
16
16
|
export { integrityOf } from './integrity.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EACL,YAAY,EAGZ,SAAS,EAET,WAAW,EACX,aAAa,EAEb,YAAY,EAGZ,QAAQ,GAET,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAAE,WAAW,EAAc,eAAe,EAAqB,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC/H,OAAO,EAAE,IAAI,EAAc,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAChG,OAAO,EAEL,iBAAiB,EAGjB,UAAU,GAKX,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,mBAAmB,EAEnB,YAAY,EACZ,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,WAAW,EAEX,aAAa,GAEd,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,WAAW,EAEX,gBAAgB,EAChB,UAAU,EACV,OAAO,EACP,mBAAmB,EACnB,iBAAiB,EAGjB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAuB,WAAW,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EACL,YAAY,EAGZ,SAAS,EAET,WAAW,EACX,aAAa,EAEb,YAAY,EAGZ,QAAQ,GAET,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAAE,WAAW,EAAc,eAAe,EAAqB,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC/H,OAAO,EAAE,IAAI,EAAc,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAChG,OAAO,EAEL,iBAAiB,EAGjB,UAAU,GAKX,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,mBAAmB,EAEnB,YAAY,EACZ,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,WAAW,EAEX,aAAa,GAEd,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,WAAW,EAEX,gBAAgB,EAChB,UAAU,EACV,OAAO,EACP,mBAAmB,EACnB,iBAAiB,EAGjB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAuB,WAAW,EAAE,WAAW,EAAoE,KAAK,EAAE,MAAM,aAAa,CAAC;AACrJ,OAAO,EAAmB,gBAAgB,EAAE,cAAc,EAAiB,aAAa,EAAc,MAAM,SAAS,CAAC;AACtH,OAAO,EAA8C,QAAQ,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACzF,OAAO,EAEL,QAAQ,EACR,SAAS,EACT,UAAU,EACV,WAAW,EACX,cAAc,EAGd,SAAS,EACT,SAAS,EAET,SAAS,EACT,cAAc,GAEf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAiC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAqB,MAAM,WAAW,CAAC;AAC9H,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAe,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC1J,OAAO,EAAE,aAAa,EAAE,eAAe,EAA+D,KAAK,EAAE,MAAM,YAAY,CAAC;AAChI,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAEL,aAAa,EACb,KAAK,EACL,KAAK,EAGL,SAAS,EACT,OAAO,EAEP,QAAQ,EACR,gBAAgB,GAIjB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,IAAI,EAA4B,OAAO,EAAqB,MAAM,YAAY,CAAC;AAGxG,8EAA8E;AAC9E,MAAM,UAAU,YAAY,CAAuC,MAAS;IAC1E,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["/**\n * The library. Everything the CLI does is reachable from here, and the\n * CLI is built from nothing else.\n */\n\nexport { applyPlan, ConflictError, TransactionError } from './apply.js';\nexport {\n applyWaivers,\n type ArchiveRequest,\n type FileOp,\n openTasks,\n type Plan,\n planArchive,\n planUnarchive,\n type PullRequest,\n renderBanner,\n type UnarchiveRequest,\n type Unmeasured,\n WAIVABLE,\n type Waiver,\n} from './archive.js';\nexport { BANNER_CLOSE, BANNER_OPEN, type Brief, BUILT_IN_FIELDS, type FieldProblem, idFromName, parseBrief } from './brief.js';\nexport { main, type CliIO, EXIT_ERROR, EXIT_FAILED, EXIT_OK, HELP, UsageError } from './cli.js';\nexport {\n type Collision,\n collisionFindings,\n type CollisionOptions,\n type CollisionReport,\n collisions,\n type Overlap,\n type SharedDirectory,\n type UndecidedPair,\n type WaveMatrix,\n} from './collisions.js';\nexport {\n BANNER_PLACEHOLDERS,\n type Config,\n CONFIG_FILES,\n CONFIG_SCHEMA,\n ConfigError,\n configJsonSchema,\n DEFAULT_CONFIG,\n parseConfig,\n type PluginReference,\n resolveConfig,\n type SectionRule,\n} from './config.js';\nexport {\n buildCorpus,\n type Corpus,\n dependencyCycles,\n findBriefs,\n isReady,\n pendingDependencies,\n resolveDependency,\n type SourceFile,\n type Waiting,\n waitingOnDeferred,\n} from './corpus.js';\nexport { type ArchiveOptions, BriefEngine, EngineError, type EngineSetup, type Inputs, type NewOptions, type OpenOptions, today } from './engine.js';\nexport { type FileSystem, MemoryFileSystem, NodeFileSystem, type NotAFile, NotAFileError, type Reads } from './fs.js';\nexport { type CommitInfo, type FileChange, type Git, GitError, NodeGit } from './git.js';\nexport {\n type Glob,\n globBase,\n globBases,\n globCovers,\n globWitness,\n intersectGlobs,\n type Literal,\n type LiteralReading,\n matchGlob,\n parseGlob,\n type ParseOptions,\n readingIn,\n WITNESS_BUDGET,\n type Witness,\n} from './glob.js';\nexport { integrityOf } from './integrity.js';\nexport { failing, lint, type LintOptions, type Plugin, ruleIds, sortFindings, summarise, type WaiveContext } from './lint.js';\nexport { asPlugin, asWaivers, loadPlugins } from './plugins.js';\nexport { type Format, FORMATS, githubCommands, gitlabCodeQuality, JSON_SCHEMA_VERSION, matrixJson, sarif, scheduleJson, sectionsJson } from './report.js';\nexport { ARCHIVE_RULES, COLLISION_RULES, type Rule, type RuleContext, type RuleInfo, type RuleResult, RULES } from './rules.js';\nexport { nextId, renderNewBrief } from './scaffold.js';\nexport {\n type Breach,\n breachReasons,\n holds,\n moves,\n type Passed,\n type Placement,\n planWaves,\n reasons,\n type Schedule,\n schedule,\n scheduleFindings,\n type ScheduleOptions,\n type Unplaced,\n type WavePlan,\n} from './schedule.js';\nexport { contradictions, meet, type Meeting, type Scope, scopeOf, type ScopePattern } from './scope.js';\nexport type { Finding, Phase, Severity, SeveritySetting, Status } from './types.js';\n\n/** Types a plugin author writes against, with inference for the rule list. */\nexport function definePlugin<T extends import('./lint.js').Plugin>(plugin: T): T {\n return plugin;\n}\n"]}
|
package/dist/plugins.js
CHANGED
|
@@ -14,6 +14,7 @@ import { createRequire } from 'node:module';
|
|
|
14
14
|
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
15
15
|
import { pathToFileURL } from 'node:url';
|
|
16
16
|
import { ConfigError } from './config.js';
|
|
17
|
+
import { notAFile } from './fs.js';
|
|
17
18
|
/** Asked about whatever a module exported as a severity, so it is a set of anything. */
|
|
18
19
|
const SEVERITIES = new Set(['off', 'note', 'warning', 'error']);
|
|
19
20
|
/** The conditions Node matches in "exports" when it imports a module. */
|
|
@@ -87,8 +88,19 @@ export function exportsTarget(exports, subpath) {
|
|
|
87
88
|
}
|
|
88
89
|
return best === undefined ? null : (exportTarget(map[best.key], best.match) ?? null);
|
|
89
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Refuses a path that is not a file before it is read or imported: a named
|
|
93
|
+
* pipe as a plugin's module, or as the package.json its package is found
|
|
94
|
+
* by, had the run wait without end (ADR-0003, amended 2026-10-24).
|
|
95
|
+
*/
|
|
96
|
+
async function onlyAFile(path) {
|
|
97
|
+
const what = await notAFile(path);
|
|
98
|
+
if (what !== null)
|
|
99
|
+
throw new Error(`${path} is ${what}; only a file is read`);
|
|
100
|
+
}
|
|
90
101
|
/** A package.json, `undefined` when there is none to read. */
|
|
91
102
|
async function manifest(path) {
|
|
103
|
+
await onlyAFile(path);
|
|
92
104
|
try {
|
|
93
105
|
// The encoding decides nothing here either, for the reason `version` in cli.ts has.
|
|
94
106
|
return JSON.parse(await readFile(path, 'utf8'));
|
|
@@ -135,10 +147,11 @@ async function resolvePackage(specifier, root) {
|
|
|
135
147
|
}
|
|
136
148
|
return createRequire(join(root, 'package.json')).resolve(specifier);
|
|
137
149
|
}
|
|
138
|
-
|
|
150
|
+
/** The file a specifier names, from the root. */
|
|
151
|
+
async function modulePath(specifier, root) {
|
|
139
152
|
if (specifier.startsWith('.') || isAbsolute(specifier))
|
|
140
|
-
return
|
|
141
|
-
return
|
|
153
|
+
return resolve(root, specifier);
|
|
154
|
+
return resolvePackage(specifier, root);
|
|
142
155
|
}
|
|
143
156
|
function isRule(value) {
|
|
144
157
|
if (typeof value !== 'object' || value === null)
|
|
@@ -201,7 +214,9 @@ export async function loadPlugins(references, root) {
|
|
|
201
214
|
for (const reference of references) {
|
|
202
215
|
let loaded;
|
|
203
216
|
try {
|
|
204
|
-
|
|
217
|
+
const path = await modulePath(reference.module, root);
|
|
218
|
+
await onlyAFile(path);
|
|
219
|
+
loaded = (await import(pathToFileURL(path).href));
|
|
205
220
|
}
|
|
206
221
|
catch (error) {
|
|
207
222
|
throw new ConfigError(reference.module, [`could not be loaded: ${error.message}`]);
|
package/dist/plugins.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugins.js","sourceRoot":"","sources":["../src/plugins.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Loading rule plugins named by the configuration.\n *\n * spec-brief does not know how another tool signs a departure, records a\n * reproducer or measures a blast radius, and should not: the tool that\n * defines a format is the one that can check it. A plugin is a module whose\n * default export is a plugin object, or a function of the configured options\n * that returns one. Loading one runs its code, exactly as loading an ESLint\n * configuration does; the configuration belongs to the repository, and so\n * does that trust.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { createRequire } from 'node:module';\nimport { dirname, isAbsolute, join, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport type { Waiver } from './archive.js';\nimport type { PluginReference } from './config.js';\nimport { ConfigError } from './config.js';\nimport type { Plugin } from './lint.js';\nimport type { Rule } from './rules.js';\n\n/** Asked about whatever a module exported as a severity, so it is a set of anything. */\nconst SEVERITIES: ReadonlySet<unknown> = new Set(['off', 'note', 'warning', 'error']);\n\n/** The conditions Node matches in \"exports\" when it imports a module. */\nconst IMPORT_CONDITIONS = new Set(['node', 'import', 'module-sync', 'default']);\n\n/**\n * A target of \"exports\": a path inside the package, `null` where the package\n * refuses the subpath, `undefined` where no condition matched and a fallback\n * may still. A target that is not a \"./\" path, or that climbs out of the\n * package or into another, is refused, as Node refuses it: its segments are\n * split on a backslash too, since `join` reads one as a separator on Windows,\n * where \"pkg/..\\..\\outside\" would otherwise climb out of node_modules.\n */\nfunction exportTarget(value: unknown, match: string | null): string | null | undefined {\n if (typeof value === 'string') {\n if (!value.startsWith('./')) return null;\n const path = match === null ? value : value.replaceAll('*', match);\n const parts = path.slice(2).split(/[\\\\/]/);\n return parts.some((part) => part === '..' || part === '.' || part.toLowerCase() === 'node_modules') ? null : path;\n }\n if (Array.isArray(value)) {\n for (const item of value) {\n const path = exportTarget(item, match);\n if (typeof path === 'string') return path;\n }\n return null;\n }\n if (typeof value === 'object' && value !== null) {\n for (const [condition, item] of Object.entries(value)) {\n if (!IMPORT_CONDITIONS.has(condition)) continue;\n const path = exportTarget(item, match);\n if (path !== undefined) return path;\n }\n return undefined;\n }\n return null;\n}\n\n/**\n * The file a package's \"exports\" gives a subpath (`.` or `./x`) under the\n * conditions of an import, or `null` when it gives none: Node's\n * PACKAGE_EXPORTS_RESOLVE. An exact key wins; then the \"*\" pattern with the\n * longest prefix, and of those the longest key.\n */\nexport function exportsTarget(exports: unknown, subpath: string): string | null {\n // Only a map of subpaths has a key that starts with a dot: a path's and a list's keys are their indices.\n const keys = Object.keys(Object(exports) as object);\n const dotted = keys.filter((key) => key.startsWith('.'));\n if (dotted.length === 0) return subpath === '.' ? (exportTarget(exports, null) ?? null) : null;\n // Subpaths beside conditions is a package Node refuses to read.\n if (dotted.length !== keys.length) return null;\n const map = exports as Record<string, unknown>;\n if (!subpath.includes('*') && Object.hasOwn(map, subpath)) return exportTarget(map[subpath], null) ?? null;\n let best: { readonly key: string; readonly prefix: number; readonly match: string } | undefined;\n for (const key of dotted) {\n const star = key.indexOf('*');\n if (star === -1 || key.includes('*', star + 1)) continue;\n const suffix = key.slice(star + 1);\n if (subpath.length < key.length || !subpath.startsWith(key.slice(0, star)) || !subpath.endsWith(suffix)) continue;\n // Two keys of one prefix and one length that both match are one key, so the lengths never tie.\n if (best === undefined || star > best.prefix || (star === best.prefix && key.length > best.key.length)) {\n best = { key, prefix: star, match: subpath.slice(star, subpath.length - suffix.length) };\n }\n }\n return best === undefined ? null : (exportTarget(map[best.key], best.match) ?? null);\n}\n\n/** A package.json, `undefined` when there is none to read. */\nasync function manifest(path: string): Promise<Record<string, unknown> | undefined> {\n try {\n // The encoding decides nothing here either, for the reason `version` in cli.ts has.\n return JSON.parse(await readFile(path, 'utf8'));\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT' || (error as NodeJS.ErrnoException).code === 'ENOTDIR') return undefined;\n throw error;\n }\n}\n\n/**\n * The file a package specifier names, found from the root the way an import\n * there would find it. `require.resolve` reads \"exports\" under the require\n * conditions, so a package that exports only for \"import\" - an ESM-only\n * plugin - could not be found; Node's `import.meta.resolve` resolves only\n * from the module calling it, not from the root. So the nearest\n * node_modules/<name> is found here and its \"exports\" read under the import\n * conditions; a package with no \"exports\" resolves as it always has.\n */\nasync function resolvePackage(specifier: string, root: string): Promise<string> {\n const slash = specifier.indexOf('/', specifier.startsWith('@') ? specifier.indexOf('/') + 1 : 0);\n const name = slash === -1 ? specifier : specifier.slice(0, slash);\n const subpath = slash === -1 ? '.' : `.${specifier.slice(slash)}`;\n let directory = root;\n // Bounded by the depth of the path: each pass moves one directory up, and\n // the search stops at the top, so no path reaches the bound and one pass\n // more or fewer of it is the same search.\n for (let depth = 0; depth < 256; depth += 1) {\n const packageDirectory = join(directory, 'node_modules', name);\n const found = await manifest(join(packageDirectory, 'package.json'));\n if (found !== undefined) {\n if (found['exports'] === undefined || found['exports'] === null) break;\n const target = exportsTarget(found['exports'], subpath);\n if (target === null) throw new Error(`${name} exports no \"${subpath}\" for import`);\n return join(packageDirectory, target);\n }\n const parent = dirname(directory);\n // Stopping at the top saves asking it again until the bound: the answer is the same.\n if (parent === directory) break;\n directory = parent;\n }\n return createRequire(join(root, 'package.json')).resolve(specifier);\n}\n\nasync function specifierUrl(specifier: string, root: string): Promise<string> {\n if (specifier.startsWith('.') || isAbsolute(specifier)) return pathToFileURL(resolve(root, specifier)).href;\n return pathToFileURL(await resolvePackage(specifier, root)).href;\n}\n\nfunction isRule(value: unknown): value is Rule {\n if (typeof value !== 'object' || value === null) return false;\n const rule = value as Record<string, unknown>;\n return (\n typeof rule['id'] === 'string' &&\n /^[a-z0-9][a-z0-9-]*$/.test(rule['id']) &&\n typeof rule['description'] === 'string' &&\n SEVERITIES.has(rule['severity']) &&\n typeof rule['check'] === 'function'\n );\n}\n\n/** Checks the shape a module exported, since nothing typed it on the way in. */\nexport function asPlugin(value: unknown, module: string, options: unknown): Plugin {\n const problems: string[] = [];\n if (typeof value !== 'object' || value === null) {\n throw new ConfigError(module, ['does not export a plugin: expected an object with \"name\" and \"rules\"']);\n }\n const candidate = value as Record<string, unknown>;\n const name = candidate['name'];\n const rules = candidate['rules'];\n const waive = candidate['waive'];\n if (typeof name !== 'string' || !/^[a-z0-9@][a-z0-9@/._-]*$/i.test(name)) problems.push('\"name\" must be a plain identifier');\n if (!Array.isArray(rules)) problems.push('\"rules\" must be a list');\n else {\n rules.forEach((rule, i) => {\n if (!isRule(rule)) problems.push(`rules[${i}] needs a lower-case \"id\", a \"description\", a \"severity\" and a \"check\" function`);\n });\n }\n if (waive !== undefined && typeof waive !== 'function') problems.push('\"waive\" must be a function');\n if (problems.length > 0) throw new ConfigError(module, problems);\n const plugin: Plugin = { name: name as string, rules: rules as Rule[], options };\n return waive === undefined ? plugin : { ...plugin, waive: waive as NonNullable<Plugin['waive']> };\n}\n\n/**\n * Checks what a `waive` hook returned: a list of `{ rule, path, reason }`,\n * each a string. A hook that returns nothing waives nothing: a function that\n * found no ruling and fell off its end has answered, not failed.\n */\nexport function asWaivers(value: unknown, plugin: string): Waiver[] {\n const isWaiver = (item: unknown): item is Waiver => {\n if (typeof item !== 'object' || item === null) return false;\n const w = item as Record<string, unknown>;\n return typeof w['rule'] === 'string' && typeof w['path'] === 'string' && typeof w['reason'] === 'string';\n };\n if (value === undefined) return [];\n if (!Array.isArray(value) || !value.every(isWaiver)) {\n throw new ConfigError(`plugin \"${plugin}\"`, ['\"waive\" must return a list of { rule, path, reason }, each a string']);\n }\n return value.map((w) => ({ rule: w.rule, path: w.path, reason: w.reason }));\n}\n\nexport async function loadPlugins(references: readonly PluginReference[], root: string): Promise<Plugin[]> {\n const plugins: Plugin[] = [];\n for (const reference of references) {\n let loaded: Record<string, unknown>;\n try {\n loaded = (await import(await specifierUrl(reference.module, root))) as Record<string, unknown>;\n } catch (error) {\n throw new ConfigError(reference.module, [`could not be loaded: ${(error as Error).message}`]);\n }\n const exported = loaded['default'] ?? loaded['plugin'];\n const value = typeof exported === 'function' ? await (exported as (o: unknown) => unknown)(reference.options) : exported;\n plugins.push(asPlugin(value, reference.module, reference.options));\n }\n const names = plugins.map((p) => p.name);\n const repeated = names.filter((n, i) => names.indexOf(n) !== i);\n if (repeated.length > 0) throw new ConfigError('plugins', [`two plugins are named \"${repeated[0] as string}\"`]);\n return plugins;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"plugins.js","sourceRoot":"","sources":["../src/plugins.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Loading rule plugins named by the configuration.\n *\n * spec-brief does not know how another tool signs a departure, records a\n * reproducer or measures a blast radius, and should not: the tool that\n * defines a format is the one that can check it. A plugin is a module whose\n * default export is a plugin object, or a function of the configured options\n * that returns one. Loading one runs its code, exactly as loading an ESLint\n * configuration does; the configuration belongs to the repository, and so\n * does that trust.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { createRequire } from 'node:module';\nimport { dirname, isAbsolute, join, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport type { Waiver } from './archive.js';\nimport type { PluginReference } from './config.js';\nimport { ConfigError } from './config.js';\nimport { notAFile } from './fs.js';\nimport type { Plugin } from './lint.js';\nimport type { Rule } from './rules.js';\n\n/** Asked about whatever a module exported as a severity, so it is a set of anything. */\nconst SEVERITIES: ReadonlySet<unknown> = new Set(['off', 'note', 'warning', 'error']);\n\n/** The conditions Node matches in \"exports\" when it imports a module. */\nconst IMPORT_CONDITIONS = new Set(['node', 'import', 'module-sync', 'default']);\n\n/**\n * A target of \"exports\": a path inside the package, `null` where the package\n * refuses the subpath, `undefined` where no condition matched and a fallback\n * may still. A target that is not a \"./\" path, or that climbs out of the\n * package or into another, is refused, as Node refuses it: its segments are\n * split on a backslash too, since `join` reads one as a separator on Windows,\n * where \"pkg/..\\..\\outside\" would otherwise climb out of node_modules.\n */\nfunction exportTarget(value: unknown, match: string | null): string | null | undefined {\n if (typeof value === 'string') {\n if (!value.startsWith('./')) return null;\n const path = match === null ? value : value.replaceAll('*', match);\n const parts = path.slice(2).split(/[\\\\/]/);\n return parts.some((part) => part === '..' || part === '.' || part.toLowerCase() === 'node_modules') ? null : path;\n }\n if (Array.isArray(value)) {\n for (const item of value) {\n const path = exportTarget(item, match);\n if (typeof path === 'string') return path;\n }\n return null;\n }\n if (typeof value === 'object' && value !== null) {\n for (const [condition, item] of Object.entries(value)) {\n if (!IMPORT_CONDITIONS.has(condition)) continue;\n const path = exportTarget(item, match);\n if (path !== undefined) return path;\n }\n return undefined;\n }\n return null;\n}\n\n/**\n * The file a package's \"exports\" gives a subpath (`.` or `./x`) under the\n * conditions of an import, or `null` when it gives none: Node's\n * PACKAGE_EXPORTS_RESOLVE. An exact key wins; then the \"*\" pattern with the\n * longest prefix, and of those the longest key.\n */\nexport function exportsTarget(exports: unknown, subpath: string): string | null {\n // Only a map of subpaths has a key that starts with a dot: a path's and a list's keys are their indices.\n const keys = Object.keys(Object(exports) as object);\n const dotted = keys.filter((key) => key.startsWith('.'));\n if (dotted.length === 0) return subpath === '.' ? (exportTarget(exports, null) ?? null) : null;\n // Subpaths beside conditions is a package Node refuses to read.\n if (dotted.length !== keys.length) return null;\n const map = exports as Record<string, unknown>;\n if (!subpath.includes('*') && Object.hasOwn(map, subpath)) return exportTarget(map[subpath], null) ?? null;\n let best: { readonly key: string; readonly prefix: number; readonly match: string } | undefined;\n for (const key of dotted) {\n const star = key.indexOf('*');\n if (star === -1 || key.includes('*', star + 1)) continue;\n const suffix = key.slice(star + 1);\n if (subpath.length < key.length || !subpath.startsWith(key.slice(0, star)) || !subpath.endsWith(suffix)) continue;\n // Two keys of one prefix and one length that both match are one key, so the lengths never tie.\n if (best === undefined || star > best.prefix || (star === best.prefix && key.length > best.key.length)) {\n best = { key, prefix: star, match: subpath.slice(star, subpath.length - suffix.length) };\n }\n }\n return best === undefined ? null : (exportTarget(map[best.key], best.match) ?? null);\n}\n\n/**\n * Refuses a path that is not a file before it is read or imported: a named\n * pipe as a plugin's module, or as the package.json its package is found\n * by, had the run wait without end (ADR-0003, amended 2026-10-24).\n */\nasync function onlyAFile(path: string): Promise<void> {\n const what = await notAFile(path);\n if (what !== null) throw new Error(`${path} is ${what}; only a file is read`);\n}\n\n/** A package.json, `undefined` when there is none to read. */\nasync function manifest(path: string): Promise<Record<string, unknown> | undefined> {\n await onlyAFile(path);\n try {\n // The encoding decides nothing here either, for the reason `version` in cli.ts has.\n return JSON.parse(await readFile(path, 'utf8'));\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT' || (error as NodeJS.ErrnoException).code === 'ENOTDIR') return undefined;\n throw error;\n }\n}\n\n/**\n * The file a package specifier names, found from the root the way an import\n * there would find it. `require.resolve` reads \"exports\" under the require\n * conditions, so a package that exports only for \"import\" - an ESM-only\n * plugin - could not be found; Node's `import.meta.resolve` resolves only\n * from the module calling it, not from the root. So the nearest\n * node_modules/<name> is found here and its \"exports\" read under the import\n * conditions; a package with no \"exports\" resolves as it always has.\n */\nasync function resolvePackage(specifier: string, root: string): Promise<string> {\n const slash = specifier.indexOf('/', specifier.startsWith('@') ? specifier.indexOf('/') + 1 : 0);\n const name = slash === -1 ? specifier : specifier.slice(0, slash);\n const subpath = slash === -1 ? '.' : `.${specifier.slice(slash)}`;\n let directory = root;\n // Bounded by the depth of the path: each pass moves one directory up, and\n // the search stops at the top, so no path reaches the bound and one pass\n // more or fewer of it is the same search.\n for (let depth = 0; depth < 256; depth += 1) {\n const packageDirectory = join(directory, 'node_modules', name);\n const found = await manifest(join(packageDirectory, 'package.json'));\n if (found !== undefined) {\n if (found['exports'] === undefined || found['exports'] === null) break;\n const target = exportsTarget(found['exports'], subpath);\n if (target === null) throw new Error(`${name} exports no \"${subpath}\" for import`);\n return join(packageDirectory, target);\n }\n const parent = dirname(directory);\n // Stopping at the top saves asking it again until the bound: the answer is the same.\n if (parent === directory) break;\n directory = parent;\n }\n return createRequire(join(root, 'package.json')).resolve(specifier);\n}\n\n/** The file a specifier names, from the root. */\nasync function modulePath(specifier: string, root: string): Promise<string> {\n if (specifier.startsWith('.') || isAbsolute(specifier)) return resolve(root, specifier);\n return resolvePackage(specifier, root);\n}\n\nfunction isRule(value: unknown): value is Rule {\n if (typeof value !== 'object' || value === null) return false;\n const rule = value as Record<string, unknown>;\n return (\n typeof rule['id'] === 'string' &&\n /^[a-z0-9][a-z0-9-]*$/.test(rule['id']) &&\n typeof rule['description'] === 'string' &&\n SEVERITIES.has(rule['severity']) &&\n typeof rule['check'] === 'function'\n );\n}\n\n/** Checks the shape a module exported, since nothing typed it on the way in. */\nexport function asPlugin(value: unknown, module: string, options: unknown): Plugin {\n const problems: string[] = [];\n if (typeof value !== 'object' || value === null) {\n throw new ConfigError(module, ['does not export a plugin: expected an object with \"name\" and \"rules\"']);\n }\n const candidate = value as Record<string, unknown>;\n const name = candidate['name'];\n const rules = candidate['rules'];\n const waive = candidate['waive'];\n if (typeof name !== 'string' || !/^[a-z0-9@][a-z0-9@/._-]*$/i.test(name)) problems.push('\"name\" must be a plain identifier');\n if (!Array.isArray(rules)) problems.push('\"rules\" must be a list');\n else {\n rules.forEach((rule, i) => {\n if (!isRule(rule)) problems.push(`rules[${i}] needs a lower-case \"id\", a \"description\", a \"severity\" and a \"check\" function`);\n });\n }\n if (waive !== undefined && typeof waive !== 'function') problems.push('\"waive\" must be a function');\n if (problems.length > 0) throw new ConfigError(module, problems);\n const plugin: Plugin = { name: name as string, rules: rules as Rule[], options };\n return waive === undefined ? plugin : { ...plugin, waive: waive as NonNullable<Plugin['waive']> };\n}\n\n/**\n * Checks what a `waive` hook returned: a list of `{ rule, path, reason }`,\n * each a string. A hook that returns nothing waives nothing: a function that\n * found no ruling and fell off its end has answered, not failed.\n */\nexport function asWaivers(value: unknown, plugin: string): Waiver[] {\n const isWaiver = (item: unknown): item is Waiver => {\n if (typeof item !== 'object' || item === null) return false;\n const w = item as Record<string, unknown>;\n return typeof w['rule'] === 'string' && typeof w['path'] === 'string' && typeof w['reason'] === 'string';\n };\n if (value === undefined) return [];\n if (!Array.isArray(value) || !value.every(isWaiver)) {\n throw new ConfigError(`plugin \"${plugin}\"`, ['\"waive\" must return a list of { rule, path, reason }, each a string']);\n }\n return value.map((w) => ({ rule: w.rule, path: w.path, reason: w.reason }));\n}\n\nexport async function loadPlugins(references: readonly PluginReference[], root: string): Promise<Plugin[]> {\n const plugins: Plugin[] = [];\n for (const reference of references) {\n let loaded: Record<string, unknown>;\n try {\n const path = await modulePath(reference.module, root);\n await onlyAFile(path);\n loaded = (await import(pathToFileURL(path).href)) as Record<string, unknown>;\n } catch (error) {\n throw new ConfigError(reference.module, [`could not be loaded: ${(error as Error).message}`]);\n }\n const exported = loaded['default'] ?? loaded['plugin'];\n const value = typeof exported === 'function' ? await (exported as (o: unknown) => unknown)(reference.options) : exported;\n plugins.push(asPlugin(value, reference.module, reference.options));\n }\n const names = plugins.map((p) => p.name);\n const repeated = names.filter((n, i) => names.indexOf(n) !== i);\n if (repeated.length > 0) throw new ConfigError('plugins', [`two plugins are named \"${repeated[0] as string}\"`]);\n return plugins;\n}\n"]}
|
package/package.json
CHANGED