From ab711f11a55cbba18e8247bd9ce0df9c3583a2fe Mon Sep 17 00:00:00 2001 From: Rodri Date: Tue, 3 Mar 2026 13:44:07 +0000 Subject: [PATCH] Add description property to wireit script configs Adds an optional `description` string field to wireit script configurations, allowing developers to document what each script does. - schema.json: adds `description` property with markdownDescription for IDE autocomplete/hover support - src/config.ts: adds `description: string | undefined` to BaseScriptConfig - src/analyzer.ts: adds #processDescription() method and wires it into script config parsing - src/test/json-schema.test.ts: adds test cases for valid/invalid values - src/test/util/package-json.ts: adds description to test PackageJson type Closes #1015 --- schema.json | 4 ++++ src/analyzer.ts | 40 +++++++++++++++++++++++++++++++++++ src/config.ts | 5 +++++ src/test/json-schema.test.ts | 26 +++++++++++++++++++++++ src/test/util/package-json.ts | 1 + 5 files changed, 76 insertions(+) diff --git a/schema.json b/schema.json index c0b2869ca..2ef866d43 100644 --- a/schema.json +++ b/schema.json @@ -8,6 +8,10 @@ "markdownDescription": "The wireit config for the npm script with this name.\n\nThe npm script should just run `wireit` with no args and its actual command should be put in the `command` property of this object.\n\nFor more info see: https://github.com/google/wireit#cleaning-output", "additionalProperties": true, "properties": { + "description": { + "markdownDescription": "A human-readable description of what this script does.\n\nFor example:\n\n```json\n\"description\": \"Compile TypeScript sources to JavaScript\"\n```", + "type": "string" + }, "clean": { "markdownDescription": "By default, `output` files are deleted before the command is run.\n\nSet `clean` to false to prevent this.\n\nSome commands, like `tsc --build`, have their own incremental run logic and only write those output files that have changed. In that case, it can be beneficial to only delete output files when one of the input files has been deleted. In that case, set `clean` to \"if-file-deleted\".\n\nFor more info see: https://github.com/google/wireit#cleaning-output", "enum": [true, false, "if-file-deleted"] diff --git a/src/analyzer.ts b/src/analyzer.ts index cc599d148..69092d9bc 100644 --- a/src/analyzer.ts +++ b/src/analyzer.ts @@ -503,6 +503,7 @@ export class Analyzer { declaringFile: packageJson.jsonFile, services: [], env: {}, + description: undefined, }; Object.assign(placeholder, remainingConfig); } @@ -632,6 +633,11 @@ export class Analyzer { ); const env = this.#processEnv(placeholder, packageJson, syntaxInfo, command); + const description = this.#processDescription( + placeholder, + packageJson, + syntaxInfo, + ); if (placeholder.failures.length > 0) { // A script with locally-determined errors doesn't get upgraded to @@ -658,6 +664,7 @@ export class Analyzer { declaringFile: packageJson.jsonFile, services: [], env, + description, }; Object.assign(placeholder, remainingConfig); } @@ -945,6 +952,39 @@ export class Analyzer { return defaultValue; } + #processDescription( + placeholder: UnvalidatedConfig, + packageJson: PackageJson, + syntaxInfo: ScriptSyntaxInfo, + ): string | undefined { + if (syntaxInfo.wireitConfigNode == null) { + return undefined; + } + const node = findNodeAtLocation(syntaxInfo.wireitConfigNode, [ + 'description', + ]); + if (node === undefined) { + return undefined; + } + if (typeof node.value === 'string') { + return node.value; + } + placeholder.failures.push({ + type: 'failure', + reason: 'invalid-config-syntax', + script: placeholder, + diagnostic: { + severity: 'error', + message: `Must be a string`, + location: { + file: packageJson.jsonFile, + range: {length: node.length, offset: node.offset}, + }, + }, + }); + return undefined; + } + #processFiles( placeholder: UnvalidatedConfig, packageJson: PackageJson, diff --git a/src/config.ts b/src/config.ts index 59a7d7fb0..e4c823f0b 100644 --- a/src/config.ts +++ b/src/config.ts @@ -207,6 +207,11 @@ interface BaseScriptConfig extends ScriptReference { */ configAstNode: NamedAstNode | undefined; + /** + * A human-readable description of what this script does. + */ + description: string | undefined; + /** The parsed JSON file that declared this script. */ declaringFile: JsonFile; failures: Failure[]; diff --git a/src/test/json-schema.test.ts b/src/test/json-schema.test.ts index 7db0cc7fd..ce78759f8 100644 --- a/src/test/json-schema.test.ts +++ b/src/test/json-schema.test.ts @@ -82,6 +82,7 @@ test('a script with all fields set is valid', () => { wireit: { a: { command: 'b', + description: 'A description.', dependencies: ['c', {script: 'c', cascade: false}], files: ['d'], output: ['e'], @@ -266,4 +267,29 @@ test('dependencies[i].cascade must be boolean', () => { ); }); +test('description is valid when set to a string', () => { + shouldValidate({ + wireit: { + a: { + command: 'b', + description: 'A human-readable description of this script.', + }, + }, + }); +}); + +test('description must be a string', () => { + expectValidationErrors( + { + wireit: { + a: { + command: 'b', + description: 123, + }, + }, + }, + ['instance.wireit.a.description is not of a type(s) string'], + ); +}); + test.run(); diff --git a/src/test/util/package-json.ts b/src/test/util/package-json.ts index 5fea420f4..6a61f990a 100644 --- a/src/test/util/package-json.ts +++ b/src/test/util/package-json.ts @@ -16,6 +16,7 @@ export interface PackageJson { wireit?: { [scriptName: string]: { command?: string; + description?: string; dependencies?: Array; files?: string[]; output?: string[];