From 4561041b80af0398a8ff8273a1f15d6c5504e540 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Mon, 23 Feb 2026 13:42:59 -0800 Subject: [PATCH 01/17] Introduce the heft-image-typings-generator-plugin. --- README.md | 1 + .../main_2026-02-23-21-40.json | 10 +++ .../config/subspaces/default/pnpm-lock.yaml | 18 ++++- .../config/subspaces/default/repo-state.json | 2 +- .../.npmignore | 34 ++++++++++ .../LICENSE | 24 +++++++ .../README.md | 65 +++++++++++++++++++ .../config/heft.json | 27 ++++++++ .../config/jest.config.json | 3 + .../config/rig.json | 7 ++ .../eslint.config.js | 18 +++++ .../heft-plugin.json | 10 +++ .../package.json | 46 +++++++++++++ .../src/ImageTypingsGeneratorHeftPlugin.ts | 48 ++++++++++++++ ...pings-generator-plugin-options.schema.json | 26 ++++++++ .../tsconfig.json | 3 + rush.json | 6 ++ 17 files changed, 346 insertions(+), 2 deletions(-) create mode 100644 common/changes/@rushstack/heft-image-typings-generator-plugin/main_2026-02-23-21-40.json create mode 100644 heft-plugins/heft-image-typings-generator-plugin/.npmignore create mode 100644 heft-plugins/heft-image-typings-generator-plugin/LICENSE create mode 100644 heft-plugins/heft-image-typings-generator-plugin/README.md create mode 100644 heft-plugins/heft-image-typings-generator-plugin/config/heft.json create mode 100644 heft-plugins/heft-image-typings-generator-plugin/config/jest.config.json create mode 100644 heft-plugins/heft-image-typings-generator-plugin/config/rig.json create mode 100644 heft-plugins/heft-image-typings-generator-plugin/eslint.config.js create mode 100644 heft-plugins/heft-image-typings-generator-plugin/heft-plugin.json create mode 100644 heft-plugins/heft-image-typings-generator-plugin/package.json create mode 100644 heft-plugins/heft-image-typings-generator-plugin/src/ImageTypingsGeneratorHeftPlugin.ts create mode 100644 heft-plugins/heft-image-typings-generator-plugin/src/schemas/image-typings-generator-plugin-options.schema.json create mode 100644 heft-plugins/heft-image-typings-generator-plugin/tsconfig.json diff --git a/README.md b/README.md index be4cb6d41cb..5b31b04f277 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ These GitHub repositories provide supplementary resources for Rush Stack: | [/eslint/eslint-plugin-security](./eslint/eslint-plugin-security/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Feslint-plugin-security.svg)](https://badge.fury.io/js/%40rushstack%2Feslint-plugin-security) | [changelog](./eslint/eslint-plugin-security/CHANGELOG.md) | [@rushstack/eslint-plugin-security](https://www.npmjs.com/package/@rushstack/eslint-plugin-security) | | [/heft-plugins/heft-api-extractor-plugin](./heft-plugins/heft-api-extractor-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-api-extractor-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-api-extractor-plugin) | [changelog](./heft-plugins/heft-api-extractor-plugin/CHANGELOG.md) | [@rushstack/heft-api-extractor-plugin](https://www.npmjs.com/package/@rushstack/heft-api-extractor-plugin) | | [/heft-plugins/heft-dev-cert-plugin](./heft-plugins/heft-dev-cert-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-dev-cert-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-dev-cert-plugin) | [changelog](./heft-plugins/heft-dev-cert-plugin/CHANGELOG.md) | [@rushstack/heft-dev-cert-plugin](https://www.npmjs.com/package/@rushstack/heft-dev-cert-plugin) | +| [/heft-plugins/heft-image-typings-generator-plugin](./heft-plugins/heft-image-typings-generator-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-image-typings-generator-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-image-typings-generator-plugin) | [changelog](./heft-plugins/heft-image-typings-generator-plugin/CHANGELOG.md) | [@rushstack/heft-image-typings-generator-plugin](https://www.npmjs.com/package/@rushstack/heft-image-typings-generator-plugin) | | [/heft-plugins/heft-isolated-typescript-transpile-plugin](./heft-plugins/heft-isolated-typescript-transpile-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-isolated-typescript-transpile-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-isolated-typescript-transpile-plugin) | [changelog](./heft-plugins/heft-isolated-typescript-transpile-plugin/CHANGELOG.md) | [@rushstack/heft-isolated-typescript-transpile-plugin](https://www.npmjs.com/package/@rushstack/heft-isolated-typescript-transpile-plugin) | | [/heft-plugins/heft-jest-plugin](./heft-plugins/heft-jest-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-jest-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-jest-plugin) | [changelog](./heft-plugins/heft-jest-plugin/CHANGELOG.md) | [@rushstack/heft-jest-plugin](https://www.npmjs.com/package/@rushstack/heft-jest-plugin) | | [/heft-plugins/heft-json-schema-typings-plugin](./heft-plugins/heft-json-schema-typings-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-json-schema-typings-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-json-schema-typings-plugin) | [changelog](./heft-plugins/heft-json-schema-typings-plugin/CHANGELOG.md) | [@rushstack/heft-json-schema-typings-plugin](https://www.npmjs.com/package/@rushstack/heft-json-schema-typings-plugin) | diff --git a/common/changes/@rushstack/heft-image-typings-generator-plugin/main_2026-02-23-21-40.json b/common/changes/@rushstack/heft-image-typings-generator-plugin/main_2026-02-23-21-40.json new file mode 100644 index 00000000000..aaaf9cf5dee --- /dev/null +++ b/common/changes/@rushstack/heft-image-typings-generator-plugin/main_2026-02-23-21-40.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/heft-image-typings-generator-plugin", + "comment": "Initial release.", + "type": "minor" + } + ], + "packageName": "@rushstack/heft-image-typings-generator-plugin" +} \ No newline at end of file diff --git a/common/config/subspaces/default/pnpm-lock.yaml b/common/config/subspaces/default/pnpm-lock.yaml index b992ec346ba..b4a53c36913 100644 --- a/common/config/subspaces/default/pnpm-lock.yaml +++ b/common/config/subspaces/default/pnpm-lock.yaml @@ -3167,6 +3167,22 @@ importers: specifier: workspace:* version: link:../../rigs/local-node-rig + ../../../heft-plugins/heft-image-typings-generator-plugin: + dependencies: + '@rushstack/typings-generator': + specifier: workspace:* + version: link:../../libraries/typings-generator + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + ../../../heft-plugins/heft-isolated-typescript-transpile-plugin: dependencies: '@rushstack/lookup-by-path': @@ -30145,7 +30161,7 @@ snapshots: eslint@8.57.1: dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.37.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) '@eslint-community/regexpp': 4.12.2 '@eslint/eslintrc': 2.1.4 '@eslint/js': 8.57.1 diff --git a/common/config/subspaces/default/repo-state.json b/common/config/subspaces/default/repo-state.json index 9b13d6b9d1a..dc8854a2b5e 100644 --- a/common/config/subspaces/default/repo-state.json +++ b/common/config/subspaces/default/repo-state.json @@ -1,5 +1,5 @@ // DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush. { - "pnpmShrinkwrapHash": "425296840bc63395649600913e66041583bdc287", + "pnpmShrinkwrapHash": "ad46a0d8a791a22ba6d3c72e0637cdf782624f0f", "preferredVersionsHash": "93bf435032db8da4a18734f1eaa359c12ad147c1" } diff --git a/heft-plugins/heft-image-typings-generator-plugin/.npmignore b/heft-plugins/heft-image-typings-generator-plugin/.npmignore new file mode 100644 index 00000000000..ffb155d74e6 --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/.npmignore @@ -0,0 +1,34 @@ +# THIS IS A STANDARD TEMPLATE FOR .npmignore FILES IN THIS REPO. + +# Ignore all files by default, to avoid accidentally publishing unintended files. +* + +# Use negative patterns to bring back the specific things we want to publish. +!/bin/** +!/lib/** +!/lib-*/** +!/dist/** + +!CHANGELOG.md +!CHANGELOG.json +!heft-plugin.json +!rush-plugin-manifest.json +!ThirdPartyNotice.txt + +# Ignore certain patterns that should not get published. +/dist/*.stats.* +/lib/**/test/ +/lib-*/**/test/ +*.test.js + +# NOTE: These don't need to be specified, because NPM includes them automatically. +# +# package.json +# README.md +# LICENSE + +# --------------------------------------------------------------------------- +# DO NOT MODIFY ABOVE THIS LINE! Add any project-specific overrides below. +# --------------------------------------------------------------------------- + +!/includes/** diff --git a/heft-plugins/heft-image-typings-generator-plugin/LICENSE b/heft-plugins/heft-image-typings-generator-plugin/LICENSE new file mode 100644 index 00000000000..d6ceefdec90 --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/LICENSE @@ -0,0 +1,24 @@ +@rushstack/heft-image-typings-generator-plugin + +Copyright (c) Microsoft Corporation. All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/heft-plugins/heft-image-typings-generator-plugin/README.md b/heft-plugins/heft-image-typings-generator-plugin/README.md new file mode 100644 index 00000000000..6f3cf4967a0 --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/README.md @@ -0,0 +1,65 @@ +# @rushstack/heft-image-typings-generator-plugin + +This is a Heft plugin that generates TypeScript `.d.ts` typings for image files (`.png`, `.jpg`, +`.gif`, `.svg`, etc.). Each matched image file produces a typing that exports a default string +for the image URL, enabling type-safe image imports in TypeScript projects. + +## Setup + +1. Add the plugin as a `devDependency` of your project: + + ```bash + rush add -p @rushstack/heft-image-typings-generator-plugin --dev + ``` + +2. Load the plugin in your project's **config/heft.json** configuration: + + ```jsonc + { + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "phasesByName": { + "build": { + "tasksByName": { + "image-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-image-typings-generator-plugin", + "options": { + "fileExtensions": [".png", ".jpg", ".gif", ".svg"], + "generatedTsFolder": "temp/image-typings" + // "srcFolder": "src" // (optional, defaults to "src") + } + } + } + } + } + } + } + ``` + +3. Add the generated typings folder to your **tsconfig.json** `rootDirs` so that + TypeScript can resolve the declarations: + + ```jsonc + { + "compilerOptions": { + "rootDirs": ["src", "temp/image-typings"] + } + } + ``` + +## Plugin options + +| Option | Type | Default | Description | +| ------------------- | ---------- | ------- | ----------------------------------------------------------------- | +| `fileExtensions` | `string[]` | — | **(required)** File extensions to generate typings for, e.g. `[".png", ".jpg"]`. | +| `generatedTsFolder` | `string` | — | **(required)** Output folder for the generated `.d.ts` files. | +| `srcFolder` | `string` | `"src"` | Source folder to scan for image files. | + +## Links + +- [CHANGELOG.md]( + https://github.com/microsoft/rushstack/blob/main/heft-plugins/heft-image-typings-generator-plugin/CHANGELOG.md) - Find + out what's new in the latest version +- [@rushstack/heft](https://www.npmjs.com/package/@rushstack/heft) - Heft is a config-driven toolchain that invokes popular tools such as TypeScript, ESLint, Jest, Webpack, and API Extractor. + +Heft is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/heft-plugins/heft-image-typings-generator-plugin/config/heft.json b/heft-plugins/heft-image-typings-generator-plugin/config/heft.json new file mode 100644 index 00000000000..0e52387039a --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/config/heft.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "extends": "local-node-rig/profiles/default/config/heft.json", + + "phasesByName": { + "build": { + "tasksByName": { + "copy-json-schemas": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft", + "pluginName": "copy-files-plugin", + "options": { + "copyOperations": [ + { + "sourcePath": "src/schemas", + "destinationFolders": ["temp/json-schemas/heft/v1"], + "fileExtensions": [".schema.json"], + "hardlink": true + } + ] + } + } + } + } + } + } +} diff --git a/heft-plugins/heft-image-typings-generator-plugin/config/jest.config.json b/heft-plugins/heft-image-typings-generator-plugin/config/jest.config.json new file mode 100644 index 00000000000..d1749681d90 --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/config/jest.config.json @@ -0,0 +1,3 @@ +{ + "extends": "local-node-rig/profiles/default/config/jest.config.json" +} diff --git a/heft-plugins/heft-image-typings-generator-plugin/config/rig.json b/heft-plugins/heft-image-typings-generator-plugin/config/rig.json new file mode 100644 index 00000000000..165ffb001f5 --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/heft-plugins/heft-image-typings-generator-plugin/eslint.config.js b/heft-plugins/heft-image-typings-generator-plugin/eslint.config.js new file mode 100644 index 00000000000..c15e6077310 --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/eslint.config.js @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeTrustedToolProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node-trusted-tool'); +const friendlyLocalsMixin = require('local-node-rig/profiles/default/includes/eslint/flat/mixins/friendly-locals'); + +module.exports = [ + ...nodeTrustedToolProfile, + ...friendlyLocalsMixin, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + } + } +]; diff --git a/heft-plugins/heft-image-typings-generator-plugin/heft-plugin.json b/heft-plugins/heft-image-typings-generator-plugin/heft-plugin.json new file mode 100644 index 00000000000..d1256ca283f --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/heft-plugin.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", + "taskPlugins": [ + { + "pluginName": "image-typings-plugin", + "entryPoint": "./lib-commonjs/ImageTypingsGeneratorHeftPlugin.js", + "optionsSchema": "./lib-commonjs/schemas/image-typings-generator-plugin-options.schema.json" + } + ] +} diff --git a/heft-plugins/heft-image-typings-generator-plugin/package.json b/heft-plugins/heft-image-typings-generator-plugin/package.json new file mode 100644 index 00000000000..5e642c70c1a --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/package.json @@ -0,0 +1,46 @@ +{ + "name": "@rushstack/heft-image-typings-generator-plugin", + "version": "0.0.0", + "description": "Generate typings for images.", + "scripts": { + "build": "heft build --clean", + "start": "heft test --clean --watch", + "_phase:build": "heft run --only build -- --clean" + }, + "repository": { + "type": "git", + "url": "https://github.com/microsoft/rushstack.git", + "directory": "heft-plugins/heft-image-typings-generator-plugin" + }, + "homepage": "https://rushstack.io/pages/heft/overview/", + "exports": { + "./lib/*.schema.json": "./lib-commonjs/*.schema.json", + "./lib/*": { + "types": "./lib-dts/*.d.ts", + "node": "./lib-commonjs/*.js", + "import": "./lib-esm/*.js", + "require": "./lib-commonjs/*.js" + }, + "./heft-plugin.json": "./heft-plugin.json", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "lib/*": [ + "lib-dts/*" + ] + } + }, + "license": "MIT", + "peerDependencies": { + "@rushstack/heft": "^1.2.3" + }, + "dependencies": { + "@rushstack/typings-generator": "workspace:*" + }, + "devDependencies": { + "@rushstack/heft": "workspace:*", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*" + } +} diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/ImageTypingsGeneratorHeftPlugin.ts b/heft-plugins/heft-image-typings-generator-plugin/src/ImageTypingsGeneratorHeftPlugin.ts new file mode 100644 index 00000000000..9adb0c0c96a --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/src/ImageTypingsGeneratorHeftPlugin.ts @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import path from 'node:path'; + +import type { IHeftPlugin, IHeftTaskSession, HeftConfiguration } from '@rushstack/heft'; +import { TypingsGenerator } from '@rushstack/typings-generator'; + +export type FileExtension = `.${string}`; + +const PLUGIN_NAME: string = 'image-typings-generator'; +const DEFAULT_FILE_EXTENSIONS: FileExtension[] = ['.png', '.jpg', '.jpeg', '.gif', '.svg']; + +export interface IImageTypingsGeneratorHeftPluginOptions { + fileExtensions?: FileExtension[]; + generatedTsFolder: string; + srcFolder?: string; +} + +export default class ImageTypingsGeneratorPlugin + implements IHeftPlugin +{ + public apply( + heftSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, + options: IImageTypingsGeneratorHeftPluginOptions + ): void { + const { + logger: { terminal } + } = heftSession; + const { buildFolderPath } = heftConfiguration; + const { fileExtensions = DEFAULT_FILE_EXTENSIONS, generatedTsFolder, srcFolder = 'src' } = options; + + const typingsGenerator: TypingsGenerator = new TypingsGenerator({ + fileExtensions, + // eslint-disable-next-line @typescript-eslint/naming-convention + readFile: async (filePath: string) => '', + srcFolder: path.resolve(buildFolderPath, srcFolder), + generatedTsFolder: path.resolve(buildFolderPath, generatedTsFolder), + parseAndGenerateTypings: () => 'declare const imageUrl: string;\nexport default imageUrl;', + terminal + }); + + heftSession.hooks.run.tapPromise(PLUGIN_NAME, async () => { + await typingsGenerator.generateTypingsAsync(); + }); + } +} diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/schemas/image-typings-generator-plugin-options.schema.json b/heft-plugins/heft-image-typings-generator-plugin/src/schemas/image-typings-generator-plugin-options.schema.json new file mode 100644 index 00000000000..7f0ea4f9ccd --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/src/schemas/image-typings-generator-plugin-options.schema.json @@ -0,0 +1,26 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + + "additionalProperties": false, + + "required": ["generatedTsFolder"], + "properties": { + "fileExtensions": { + "description": "File extensions to generate typings for, e.g. \".png\", \".jpg\", \".svg\". Defaults to [\".png\", \".jpg\", \".jpeg\", \".gif\", \".svg\"] if not provided.", + "type": "array", + "items": { + "type": "string", + "pattern": "\\..+" + } + }, + "generatedTsFolder": { + "description": "The output folder where generated .d.ts typings will be written.", + "type": "string" + }, + "srcFolder": { + "description": "The source folder to scan for image files. Defaults to \"src\".", + "type": "string" + } + } +} diff --git a/heft-plugins/heft-image-typings-generator-plugin/tsconfig.json b/heft-plugins/heft-image-typings-generator-plugin/tsconfig.json new file mode 100644 index 00000000000..dac21d04081 --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/local-node-rig/profiles/default/tsconfig-base.json" +} diff --git a/rush.json b/rush.json index 8d2151e9cc1..90b19beab9b 100644 --- a/rush.json +++ b/rush.json @@ -1096,6 +1096,12 @@ "reviewCategory": "libraries", "shouldPublish": true }, + { + "packageName": "@rushstack/heft-image-typings-generator-plugin", + "projectFolder": "heft-plugins/heft-image-typings-generator-plugin", + "reviewCategory": "libraries", + "shouldPublish": true + }, { "packageName": "@rushstack/heft-isolated-typescript-transpile-plugin", "projectFolder": "heft-plugins/heft-isolated-typescript-transpile-plugin", From b7f3a4b5ca00f005018768242d0c973ca35f8eab Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Mon, 23 Feb 2026 13:54:17 -0800 Subject: [PATCH 02/17] Use the new plugin in build-tests projects. --- .../heft-rspack-everything-test/config/heft.json | 10 ++++++++++ .../config/rush-project.json | 2 +- build-tests/heft-rspack-everything-test/package.json | 1 + .../src/chunks/image.d.png.ts | 6 ------ .../heft-rspack-everything-test/tsconfig.json | 3 +-- .../heft-typescript-composite-test/config/heft.json | 10 ++++++++++ .../heft-typescript-composite-test/package.json | 1 + .../src/chunks/ChunkClass.ts | 5 +++-- .../tsconfig-base.json | 2 +- .../heft-webpack4-everything-test/config/heft.json | 10 ++++++++++ .../config/rush-project.json | 2 +- .../heft-webpack4-everything-test/package.json | 1 + .../src/chunks/ChunkClass.ts | 5 +++-- .../heft-webpack4-everything-test/tsconfig.json | 2 +- .../heft-webpack5-everything-test/config/heft.json | 10 ++++++++++ .../config/rush-project.json | 2 +- .../heft-webpack5-everything-test/package.json | 1 + .../src/chunks/ChunkClass.ts | 5 +++-- .../src/chunks/image.d.png.ts | 6 ------ .../heft-webpack5-everything-test/tsconfig.json | 3 +-- common/config/rush/nonbrowser-approved-packages.json | 4 ++++ common/config/subspaces/default/pnpm-lock.yaml | 12 ++++++++++++ 22 files changed, 76 insertions(+), 27 deletions(-) delete mode 100644 build-tests/heft-rspack-everything-test/src/chunks/image.d.png.ts delete mode 100644 build-tests/heft-webpack5-everything-test/src/chunks/image.d.png.ts diff --git a/build-tests/heft-rspack-everything-test/config/heft.json b/build-tests/heft-rspack-everything-test/config/heft.json index 7d826b05556..9c73eefd33f 100644 --- a/build-tests/heft-rspack-everything-test/config/heft.json +++ b/build-tests/heft-rspack-everything-test/config/heft.json @@ -10,7 +10,17 @@ "cleanFiles": [{ "includeGlobs": ["dist", "lib", "lib-commonjs"] }], "tasksByName": { + "image-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-image-typings-generator-plugin", + "options": { + "fileExtensions": [".png"], + "generatedTsFolder": "temp/image-typings" + } + } + }, "typescript": { + "taskDependencies": ["image-typings"], "taskPlugin": { "pluginPackage": "@rushstack/heft-typescript-plugin" } diff --git a/build-tests/heft-rspack-everything-test/config/rush-project.json b/build-tests/heft-rspack-everything-test/config/rush-project.json index 1c0d7da9a5d..f1de027644c 100644 --- a/build-tests/heft-rspack-everything-test/config/rush-project.json +++ b/build-tests/heft-rspack-everything-test/config/rush-project.json @@ -4,7 +4,7 @@ "operationSettings": [ { "operationName": "_phase:build", - "outputFolderNames": ["lib-esm", "lib-commonjs", "dist"] + "outputFolderNames": ["lib-esm", "lib-commonjs", "dist", "temp/image-typings"] }, { "operationName": "_phase:test", diff --git a/build-tests/heft-rspack-everything-test/package.json b/build-tests/heft-rspack-everything-test/package.json index 081fc7ac997..aa98a6594e4 100644 --- a/build-tests/heft-rspack-everything-test/package.json +++ b/build-tests/heft-rspack-everything-test/package.json @@ -12,6 +12,7 @@ }, "devDependencies": { "@rushstack/heft-dev-cert-plugin": "workspace:*", + "@rushstack/heft-image-typings-generator-plugin": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", diff --git a/build-tests/heft-rspack-everything-test/src/chunks/image.d.png.ts b/build-tests/heft-rspack-everything-test/src/chunks/image.d.png.ts deleted file mode 100644 index f38a285dfd9..00000000000 --- a/build-tests/heft-rspack-everything-test/src/chunks/image.d.png.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -declare const path: string; - -export default path; diff --git a/build-tests/heft-rspack-everything-test/tsconfig.json b/build-tests/heft-rspack-everything-test/tsconfig.json index e36104a9897..619397b3a75 100644 --- a/build-tests/heft-rspack-everything-test/tsconfig.json +++ b/build-tests/heft-rspack-everything-test/tsconfig.json @@ -3,9 +3,8 @@ "compilerOptions": { "outDir": "lib-esm", - "rootDir": "src", + "rootDirs": ["src", "temp/image-typings"], - "allowArbitraryExtensions": true, "forceConsistentCasingInFileNames": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, diff --git a/build-tests/heft-typescript-composite-test/config/heft.json b/build-tests/heft-typescript-composite-test/config/heft.json index c0cc9f94228..a49cba975a2 100644 --- a/build-tests/heft-typescript-composite-test/config/heft.json +++ b/build-tests/heft-typescript-composite-test/config/heft.json @@ -10,7 +10,17 @@ "cleanFiles": [{ "includeGlobs": ["lib", "lib-commonjs"] }], "tasksByName": { + "image-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-image-typings-generator-plugin", + "options": { + "fileExtensions": [".png"], + "generatedTsFolder": "temp/image-typings" + } + } + }, "typescript": { + "taskDependencies": ["image-typings"], "taskPlugin": { "pluginPackage": "@rushstack/heft-typescript-plugin" } diff --git a/build-tests/heft-typescript-composite-test/package.json b/build-tests/heft-typescript-composite-test/package.json index 48dba7975a5..cf8f5cf66db 100644 --- a/build-tests/heft-typescript-composite-test/package.json +++ b/build-tests/heft-typescript-composite-test/package.json @@ -11,6 +11,7 @@ }, "devDependencies": { "@rushstack/heft": "workspace:*", + "@rushstack/heft-image-typings-generator-plugin": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", diff --git a/build-tests/heft-typescript-composite-test/src/chunks/ChunkClass.ts b/build-tests/heft-typescript-composite-test/src/chunks/ChunkClass.ts index ddbf7d148c7..50e541f593d 100644 --- a/build-tests/heft-typescript-composite-test/src/chunks/ChunkClass.ts +++ b/build-tests/heft-typescript-composite-test/src/chunks/ChunkClass.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import image from './image.png'; + export class ChunkClass { public doStuff(): void { // eslint-disable-next-line no-console @@ -8,7 +10,6 @@ export class ChunkClass { } public getImageUrl(): string { - // eslint-disable-next-line @typescript-eslint/no-require-imports - return require('./image.png'); + return image; } } diff --git a/build-tests/heft-typescript-composite-test/tsconfig-base.json b/build-tests/heft-typescript-composite-test/tsconfig-base.json index b5bf6469742..5b94a0d7cd7 100644 --- a/build-tests/heft-typescript-composite-test/tsconfig-base.json +++ b/build-tests/heft-typescript-composite-test/tsconfig-base.json @@ -3,7 +3,7 @@ "compilerOptions": { "outDir": "lib", - "rootDir": "src", + "rootDirs": ["src", "temp/image-typings"], "forceConsistentCasingInFileNames": true, "jsx": "react", diff --git a/build-tests/heft-webpack4-everything-test/config/heft.json b/build-tests/heft-webpack4-everything-test/config/heft.json index 2f7ae591fd6..370b30f73ae 100644 --- a/build-tests/heft-webpack4-everything-test/config/heft.json +++ b/build-tests/heft-webpack4-everything-test/config/heft.json @@ -10,7 +10,17 @@ "cleanFiles": [{ "includeGlobs": ["dist", "lib", "lib-commonjs"] }], "tasksByName": { + "image-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-image-typings-generator-plugin", + "options": { + "fileExtensions": [".png"], + "generatedTsFolder": "temp/image-typings" + } + } + }, "typescript": { + "taskDependencies": ["image-typings"], "taskPlugin": { "pluginPackage": "@rushstack/heft-typescript-plugin" } diff --git a/build-tests/heft-webpack4-everything-test/config/rush-project.json b/build-tests/heft-webpack4-everything-test/config/rush-project.json index 1c0d7da9a5d..f1de027644c 100644 --- a/build-tests/heft-webpack4-everything-test/config/rush-project.json +++ b/build-tests/heft-webpack4-everything-test/config/rush-project.json @@ -4,7 +4,7 @@ "operationSettings": [ { "operationName": "_phase:build", - "outputFolderNames": ["lib-esm", "lib-commonjs", "dist"] + "outputFolderNames": ["lib-esm", "lib-commonjs", "dist", "temp/image-typings"] }, { "operationName": "_phase:test", diff --git a/build-tests/heft-webpack4-everything-test/package.json b/build-tests/heft-webpack4-everything-test/package.json index affffe9d6f7..cec1a663956 100644 --- a/build-tests/heft-webpack4-everything-test/package.json +++ b/build-tests/heft-webpack4-everything-test/package.json @@ -11,6 +11,7 @@ }, "devDependencies": { "@rushstack/heft-dev-cert-plugin": "workspace:*", + "@rushstack/heft-image-typings-generator-plugin": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", diff --git a/build-tests/heft-webpack4-everything-test/src/chunks/ChunkClass.ts b/build-tests/heft-webpack4-everything-test/src/chunks/ChunkClass.ts index ddbf7d148c7..50e541f593d 100644 --- a/build-tests/heft-webpack4-everything-test/src/chunks/ChunkClass.ts +++ b/build-tests/heft-webpack4-everything-test/src/chunks/ChunkClass.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import image from './image.png'; + export class ChunkClass { public doStuff(): void { // eslint-disable-next-line no-console @@ -8,7 +10,6 @@ export class ChunkClass { } public getImageUrl(): string { - // eslint-disable-next-line @typescript-eslint/no-require-imports - return require('./image.png'); + return image; } } diff --git a/build-tests/heft-webpack4-everything-test/tsconfig.json b/build-tests/heft-webpack4-everything-test/tsconfig.json index 2b5b7ac9673..fca9a5d7b34 100644 --- a/build-tests/heft-webpack4-everything-test/tsconfig.json +++ b/build-tests/heft-webpack4-everything-test/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "outDir": "lib-esm", - "rootDir": "src", + "rootDirs": ["src", "temp/image-typings"], "forceConsistentCasingInFileNames": true, "jsx": "react", diff --git a/build-tests/heft-webpack5-everything-test/config/heft.json b/build-tests/heft-webpack5-everything-test/config/heft.json index db369f8f99d..703d33b5a60 100644 --- a/build-tests/heft-webpack5-everything-test/config/heft.json +++ b/build-tests/heft-webpack5-everything-test/config/heft.json @@ -10,7 +10,17 @@ "cleanFiles": [{ "includeGlobs": ["dist", "lib", "lib-commonjs"] }], "tasksByName": { + "image-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-image-typings-generator-plugin", + "options": { + "fileExtensions": [".png"], + "generatedTsFolder": "temp/image-typings" + } + } + }, "typescript": { + "taskDependencies": ["image-typings"], "taskPlugin": { "pluginPackage": "@rushstack/heft-typescript-plugin" } diff --git a/build-tests/heft-webpack5-everything-test/config/rush-project.json b/build-tests/heft-webpack5-everything-test/config/rush-project.json index 1c0d7da9a5d..f1de027644c 100644 --- a/build-tests/heft-webpack5-everything-test/config/rush-project.json +++ b/build-tests/heft-webpack5-everything-test/config/rush-project.json @@ -4,7 +4,7 @@ "operationSettings": [ { "operationName": "_phase:build", - "outputFolderNames": ["lib-esm", "lib-commonjs", "dist"] + "outputFolderNames": ["lib-esm", "lib-commonjs", "dist", "temp/image-typings"] }, { "operationName": "_phase:test", diff --git a/build-tests/heft-webpack5-everything-test/package.json b/build-tests/heft-webpack5-everything-test/package.json index 204dc75de8c..fae41f8ef3c 100644 --- a/build-tests/heft-webpack5-everything-test/package.json +++ b/build-tests/heft-webpack5-everything-test/package.json @@ -13,6 +13,7 @@ }, "devDependencies": { "@rushstack/heft-dev-cert-plugin": "workspace:*", + "@rushstack/heft-image-typings-generator-plugin": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", diff --git a/build-tests/heft-webpack5-everything-test/src/chunks/ChunkClass.ts b/build-tests/heft-webpack5-everything-test/src/chunks/ChunkClass.ts index ddbf7d148c7..50e541f593d 100644 --- a/build-tests/heft-webpack5-everything-test/src/chunks/ChunkClass.ts +++ b/build-tests/heft-webpack5-everything-test/src/chunks/ChunkClass.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import image from './image.png'; + export class ChunkClass { public doStuff(): void { // eslint-disable-next-line no-console @@ -8,7 +10,6 @@ export class ChunkClass { } public getImageUrl(): string { - // eslint-disable-next-line @typescript-eslint/no-require-imports - return require('./image.png'); + return image; } } diff --git a/build-tests/heft-webpack5-everything-test/src/chunks/image.d.png.ts b/build-tests/heft-webpack5-everything-test/src/chunks/image.d.png.ts deleted file mode 100644 index f38a285dfd9..00000000000 --- a/build-tests/heft-webpack5-everything-test/src/chunks/image.d.png.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -declare const path: string; - -export default path; diff --git a/build-tests/heft-webpack5-everything-test/tsconfig.json b/build-tests/heft-webpack5-everything-test/tsconfig.json index e3c0ce2496a..1707aad311e 100644 --- a/build-tests/heft-webpack5-everything-test/tsconfig.json +++ b/build-tests/heft-webpack5-everything-test/tsconfig.json @@ -3,9 +3,8 @@ "compilerOptions": { "outDir": "lib-esm", - "rootDir": "src", + "rootDirs": ["src", "temp/image-typings"], - "allowArbitraryExtensions": true, "forceConsistentCasingInFileNames": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, diff --git a/common/config/rush/nonbrowser-approved-packages.json b/common/config/rush/nonbrowser-approved-packages.json index 3f6756df1a8..a8ae90c8dd3 100644 --- a/common/config/rush/nonbrowser-approved-packages.json +++ b/common/config/rush/nonbrowser-approved-packages.json @@ -198,6 +198,10 @@ "name": "@rushstack/heft-dev-cert-plugin", "allowedCategories": [ "libraries", "tests" ] }, + { + "name": "@rushstack/heft-image-typings-generator-plugin", + "allowedCategories": [ "tests" ] + }, { "name": "@rushstack/heft-isolated-typescript-transpile-plugin", "allowedCategories": [ "tests" ] diff --git a/common/config/subspaces/default/pnpm-lock.yaml b/common/config/subspaces/default/pnpm-lock.yaml index b4a53c36913..b83c5669541 100644 --- a/common/config/subspaces/default/pnpm-lock.yaml +++ b/common/config/subspaces/default/pnpm-lock.yaml @@ -2095,6 +2095,9 @@ importers: '@rushstack/heft-dev-cert-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-dev-cert-plugin + '@rushstack/heft-image-typings-generator-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-image-typings-generator-plugin '@rushstack/heft-jest-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-jest-plugin @@ -2249,6 +2252,9 @@ importers: '@rushstack/heft': specifier: workspace:* version: link:../../apps/heft + '@rushstack/heft-image-typings-generator-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-image-typings-generator-plugin '@rushstack/heft-jest-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-jest-plugin @@ -2408,6 +2414,9 @@ importers: '@rushstack/heft-dev-cert-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-dev-cert-plugin + '@rushstack/heft-image-typings-generator-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-image-typings-generator-plugin '@rushstack/heft-jest-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-jest-plugin @@ -2468,6 +2477,9 @@ importers: '@rushstack/heft-dev-cert-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-dev-cert-plugin + '@rushstack/heft-image-typings-generator-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-image-typings-generator-plugin '@rushstack/heft-jest-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-jest-plugin From 0be67ef7e8d83fc00b59bf2cbed191330112c9e3 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Mon, 23 Feb 2026 14:01:14 -0800 Subject: [PATCH 03/17] Add the typigns generator to the web rig. --- .../heft-web-rig-app-tutorial/src/ExampleApp.tsx | 6 ++---- ...ge-typings-generator-plugin_2026-02-23-22-01.json | 10 ++++++++++ common/config/rush/nonbrowser-approved-packages.json | 2 +- common/config/subspaces/default/pnpm-lock.yaml | 3 +++ rigs/heft-web-rig/package.json | 1 + rigs/heft-web-rig/profiles/app/config/heft.json | 12 +++++++++++- rigs/heft-web-rig/profiles/app/tsconfig-base.json | 2 +- rigs/heft-web-rig/profiles/library/config/heft.json | 12 +++++++++++- .../heft-web-rig/profiles/library/tsconfig-base.json | 2 +- rigs/heft-web-rig/shared/webpack-base.config.js | 2 +- 10 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 common/changes/@rushstack/heft-web-rig/heft-image-typings-generator-plugin_2026-02-23-22-01.json diff --git a/build-tests-samples/heft-web-rig-app-tutorial/src/ExampleApp.tsx b/build-tests-samples/heft-web-rig-app-tutorial/src/ExampleApp.tsx index 1fb8c83d1d4..7cbb226945d 100644 --- a/build-tests-samples/heft-web-rig-app-tutorial/src/ExampleApp.tsx +++ b/build-tests-samples/heft-web-rig-app-tutorial/src/ExampleApp.tsx @@ -3,6 +3,7 @@ import * as React from 'react'; import { ToggleSwitch, type IToggleEventArgs } from 'heft-web-rig-library-tutorial'; +import exampleImage from './example-image.png'; /** * This React component renders the application page. @@ -24,10 +25,7 @@ export class ExampleApp extends React.Component {

Here is an example image:

- + ); } diff --git a/common/changes/@rushstack/heft-web-rig/heft-image-typings-generator-plugin_2026-02-23-22-01.json b/common/changes/@rushstack/heft-web-rig/heft-image-typings-generator-plugin_2026-02-23-22-01.json new file mode 100644 index 00000000000..e3c919aa87d --- /dev/null +++ b/common/changes/@rushstack/heft-web-rig/heft-image-typings-generator-plugin_2026-02-23-22-01.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/heft-web-rig", + "comment": "Add `@rushstack/heft-image-typings-generator-plugin` to the `app` and `library` profiles, generating `.d.ts` typings for image files (`.png`, `.jpg`, `.jpeg`, `.gif`, `.svg`, `.ico`, `.webp`, `.avif`). This enables type-safe image imports in TypeScript without needing `allowArbitraryExtensions` or `require()` calls. Also add `.webp` and `.avif` to the webpack asset resource rule.", + "type": "minor" + } + ], + "packageName": "@rushstack/heft-web-rig" +} \ No newline at end of file diff --git a/common/config/rush/nonbrowser-approved-packages.json b/common/config/rush/nonbrowser-approved-packages.json index a8ae90c8dd3..a85e837ddad 100644 --- a/common/config/rush/nonbrowser-approved-packages.json +++ b/common/config/rush/nonbrowser-approved-packages.json @@ -200,7 +200,7 @@ }, { "name": "@rushstack/heft-image-typings-generator-plugin", - "allowedCategories": [ "tests" ] + "allowedCategories": [ "libraries", "tests" ] }, { "name": "@rushstack/heft-isolated-typescript-transpile-plugin", diff --git a/common/config/subspaces/default/pnpm-lock.yaml b/common/config/subspaces/default/pnpm-lock.yaml index b83c5669541..701e4cef28f 100644 --- a/common/config/subspaces/default/pnpm-lock.yaml +++ b/common/config/subspaces/default/pnpm-lock.yaml @@ -4738,6 +4738,9 @@ importers: '@rushstack/heft-api-extractor-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-api-extractor-plugin + '@rushstack/heft-image-typings-generator-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-image-typings-generator-plugin '@rushstack/heft-jest-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-jest-plugin diff --git a/rigs/heft-web-rig/package.json b/rigs/heft-web-rig/package.json index 93b08e3d100..59f8c01839e 100644 --- a/rigs/heft-web-rig/package.json +++ b/rigs/heft-web-rig/package.json @@ -19,6 +19,7 @@ "@microsoft/api-extractor": "workspace:*", "@rushstack/eslint-config": "workspace:*", "@rushstack/heft-api-extractor-plugin": "workspace:*", + "@rushstack/heft-image-typings-generator-plugin": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-sass-plugin": "workspace:*", diff --git a/rigs/heft-web-rig/profiles/app/config/heft.json b/rigs/heft-web-rig/profiles/app/config/heft.json index 8e80c606118..9dd36c11dbf 100644 --- a/rigs/heft-web-rig/profiles/app/config/heft.json +++ b/rigs/heft-web-rig/profiles/app/config/heft.json @@ -47,8 +47,18 @@ "pluginPackage": "@rushstack/heft-sass-plugin" } }, + "image-typings": { + "taskDependencies": ["set-browserslist-ignore-old-data-env-var"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-image-typings-generator-plugin", + "options": { + "fileExtensions": [".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp", ".avif"], + "generatedTsFolder": "temp/image-typings" + } + } + }, "typescript": { - "taskDependencies": ["sass"], + "taskDependencies": ["sass", "image-typings"], "taskPlugin": { "pluginPackage": "@rushstack/heft-typescript-plugin" } diff --git a/rigs/heft-web-rig/profiles/app/tsconfig-base.json b/rigs/heft-web-rig/profiles/app/tsconfig-base.json index 0594f2e7b70..3ba69ee3728 100644 --- a/rigs/heft-web-rig/profiles/app/tsconfig-base.json +++ b/rigs/heft-web-rig/profiles/app/tsconfig-base.json @@ -4,7 +4,7 @@ "compilerOptions": { "outDir": "../../../../../lib", "rootDir": "../../../../../src", - "rootDirs": ["../../../../../src", "../../../../../temp/sass-ts"], + "rootDirs": ["../../../../../src", "../../../../../temp/sass-ts", "../../../../../temp/image-typings"], "forceConsistentCasingInFileNames": true, "jsx": "react", diff --git a/rigs/heft-web-rig/profiles/library/config/heft.json b/rigs/heft-web-rig/profiles/library/config/heft.json index 8e80c606118..9dd36c11dbf 100644 --- a/rigs/heft-web-rig/profiles/library/config/heft.json +++ b/rigs/heft-web-rig/profiles/library/config/heft.json @@ -47,8 +47,18 @@ "pluginPackage": "@rushstack/heft-sass-plugin" } }, + "image-typings": { + "taskDependencies": ["set-browserslist-ignore-old-data-env-var"], + "taskPlugin": { + "pluginPackage": "@rushstack/heft-image-typings-generator-plugin", + "options": { + "fileExtensions": [".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp", ".avif"], + "generatedTsFolder": "temp/image-typings" + } + } + }, "typescript": { - "taskDependencies": ["sass"], + "taskDependencies": ["sass", "image-typings"], "taskPlugin": { "pluginPackage": "@rushstack/heft-typescript-plugin" } diff --git a/rigs/heft-web-rig/profiles/library/tsconfig-base.json b/rigs/heft-web-rig/profiles/library/tsconfig-base.json index 0594f2e7b70..3ba69ee3728 100644 --- a/rigs/heft-web-rig/profiles/library/tsconfig-base.json +++ b/rigs/heft-web-rig/profiles/library/tsconfig-base.json @@ -4,7 +4,7 @@ "compilerOptions": { "outDir": "../../../../../lib", "rootDir": "../../../../../src", - "rootDirs": ["../../../../../src", "../../../../../temp/sass-ts"], + "rootDirs": ["../../../../../src", "../../../../../temp/sass-ts", "../../../../../temp/image-typings"], "forceConsistentCasingInFileNames": true, "jsx": "react", diff --git a/rigs/heft-web-rig/shared/webpack-base.config.js b/rigs/heft-web-rig/shared/webpack-base.config.js index 8ca3bf5d978..e848ead26c6 100644 --- a/rigs/heft-web-rig/shared/webpack-base.config.js +++ b/rigs/heft-web-rig/shared/webpack-base.config.js @@ -205,7 +205,7 @@ function createWebpackConfig({ env, argv, projectRoot, configOverride, extractCs }, { - test: /\.(jpeg|jpg|png|gif|svg|ico|woff|woff2|ttf|eot)$/, + test: /\.(jpeg|jpg|png|gif|svg|ico|webp|avif|woff|woff2|ttf|eot)$/, // Allows import/require() to be used with an asset file. The file will be copied to the output folder, // and the import statement will return its URL. // https://webpack.js.org/guides/asset-modules/#resource-assets From a19cbe5bdf7206b6d5146679d504d63ae5395ce9 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Mon, 23 Feb 2026 14:08:01 -0800 Subject: [PATCH 04/17] Rewrite the plugin to handle general static assets. --- .../config/subspaces/default/pnpm-lock.yaml | 9 + .../heft-plugin.json | 12 +- .../package.json | 3 + .../src/BinaryAssetsPlugin.ts | 99 +++++++ .../src/ImageTypingsGeneratorHeftPlugin.ts | 48 ---- .../src/StaticAssetTypingsGenerator.ts | 260 ++++++++++++++++++ .../src/TextAssetsPlugin.ts | 138 ++++++++++ .../src/getConfigFromConfigFileAsync.ts | 48 ++++ .../schemas/binary-assets-options.schema.json | 61 ++++ ...pings-generator-plugin-options.schema.json | 26 -- .../schemas/static-asset-typings.schema.json | 30 ++ .../schemas/text-assets-options.schema.json | 29 ++ .../src/types.ts | 18 ++ 13 files changed, 704 insertions(+), 77 deletions(-) create mode 100644 heft-plugins/heft-image-typings-generator-plugin/src/BinaryAssetsPlugin.ts delete mode 100644 heft-plugins/heft-image-typings-generator-plugin/src/ImageTypingsGeneratorHeftPlugin.ts create mode 100644 heft-plugins/heft-image-typings-generator-plugin/src/StaticAssetTypingsGenerator.ts create mode 100644 heft-plugins/heft-image-typings-generator-plugin/src/TextAssetsPlugin.ts create mode 100644 heft-plugins/heft-image-typings-generator-plugin/src/getConfigFromConfigFileAsync.ts create mode 100644 heft-plugins/heft-image-typings-generator-plugin/src/schemas/binary-assets-options.schema.json delete mode 100644 heft-plugins/heft-image-typings-generator-plugin/src/schemas/image-typings-generator-plugin-options.schema.json create mode 100644 heft-plugins/heft-image-typings-generator-plugin/src/schemas/static-asset-typings.schema.json create mode 100644 heft-plugins/heft-image-typings-generator-plugin/src/schemas/text-assets-options.schema.json create mode 100644 heft-plugins/heft-image-typings-generator-plugin/src/types.ts diff --git a/common/config/subspaces/default/pnpm-lock.yaml b/common/config/subspaces/default/pnpm-lock.yaml index 701e4cef28f..11328343a83 100644 --- a/common/config/subspaces/default/pnpm-lock.yaml +++ b/common/config/subspaces/default/pnpm-lock.yaml @@ -3181,6 +3181,15 @@ importers: ../../../heft-plugins/heft-image-typings-generator-plugin: dependencies: + '@rushstack/heft-config-file': + specifier: workspace:* + version: link:../../libraries/heft-config-file + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal '@rushstack/typings-generator': specifier: workspace:* version: link:../../libraries/typings-generator diff --git a/heft-plugins/heft-image-typings-generator-plugin/heft-plugin.json b/heft-plugins/heft-image-typings-generator-plugin/heft-plugin.json index d1256ca283f..b9d0644e0c4 100644 --- a/heft-plugins/heft-image-typings-generator-plugin/heft-plugin.json +++ b/heft-plugins/heft-image-typings-generator-plugin/heft-plugin.json @@ -1,10 +1,16 @@ { "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-plugin.schema.json", + "taskPlugins": [ { - "pluginName": "image-typings-plugin", - "entryPoint": "./lib-commonjs/ImageTypingsGeneratorHeftPlugin.js", - "optionsSchema": "./lib-commonjs/schemas/image-typings-generator-plugin-options.schema.json" + "pluginName": "binary-assets-plugin", + "entryPoint": "./lib-commonjs/BinaryAssetsPlugin", + "optionsSchema": "./lib-commonjs/schemas/binary-assets-options.schema.json" + }, + { + "pluginName": "text-assets-plugin", + "entryPoint": "./lib-commonjs/TextAssetsPlugin", + "optionsSchema": "./lib-commonjs/schemas/text-assets-options.schema.json" } ] } diff --git a/heft-plugins/heft-image-typings-generator-plugin/package.json b/heft-plugins/heft-image-typings-generator-plugin/package.json index 5e642c70c1a..d52125f7430 100644 --- a/heft-plugins/heft-image-typings-generator-plugin/package.json +++ b/heft-plugins/heft-image-typings-generator-plugin/package.json @@ -36,6 +36,9 @@ "@rushstack/heft": "^1.2.3" }, "dependencies": { + "@rushstack/heft-config-file": "workspace:*", + "@rushstack/node-core-library": "workspace:*", + "@rushstack/terminal": "workspace:*", "@rushstack/typings-generator": "workspace:*" }, "devDependencies": { diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/BinaryAssetsPlugin.ts b/heft-plugins/heft-image-typings-generator-plugin/src/BinaryAssetsPlugin.ts new file mode 100644 index 00000000000..9887dd21f49 --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/src/BinaryAssetsPlugin.ts @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { HeftConfiguration, IHeftTaskSession, IHeftTaskPlugin } from '@rushstack/heft'; +import { Path } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; + +import { + createTypingsGeneratorAsync, + type IRunGeneratorOptions, + type IStaticAssetGeneratorOptions, + type IStaticAssetTypingsGenerator +} from './StaticAssetTypingsGenerator'; +import type { IStaticAssetTypingsConfigurationJson } from './types'; + +const PLUGIN_NAME: 'static-asset-typings-plugin' = 'static-asset-typings-plugin'; + +export interface IBinaryAssetsInlineConfigPluginOptions { + configType: 'inline'; + /** + * The inline configuration object. + */ + config: IStaticAssetTypingsConfigurationJson; +} + +export interface IBinaryAssetsFileConfigPluginOptions { + configType: 'file'; + /** + * The name of the riggable config file in the config/ folder. + */ + configFileName: string; +} + +export type IBinaryAssetPluginOptions = + | IBinaryAssetsInlineConfigPluginOptions + | IBinaryAssetsFileConfigPluginOptions; + +export default class BinaryAssetsPlugin implements IHeftTaskPlugin { + /** + * Generate typings for text files before TypeScript compilation. + */ + public apply( + taskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, + options: IBinaryAssetPluginOptions + ): void { + const slashNormalizedBuildFolderPath: string = Path.convertToSlashes(heftConfiguration.buildFolderPath); + + async function getVersionAndEmitOutputFilesAsync( + relativePath: string, + filePath: string, + oldVersion: string | undefined + ): Promise { + return 'versionless'; + } + + async function tryGetConfigAsync( + terminal: ITerminal, + buildFolder: string, + rigConfig: HeftConfiguration['rigConfig'] + ): Promise { + if (options?.configType === 'inline') { + return options.config; + } else { + const { getConfigFromConfigFileAsync } = await import('./getConfigFromConfigFileAsync'); + const { configFileName } = options as IBinaryAssetsFileConfigPluginOptions; + return getConfigFromConfigFileAsync(configFileName, terminal, buildFolder, rigConfig); + } + } + + const staticAssetGeneratorOptions: IStaticAssetGeneratorOptions = { + tryGetConfigAsync, + slashNormalizedBuildFolderPath, + getVersionAndEmitOutputFilesAsync + }; + + let generator: IStaticAssetTypingsGenerator | undefined | false; + + async function createAndRunGeneratorAsync(runOptions: IRunGeneratorOptions): Promise { + if (generator === undefined) { + // eslint-disable-next-line require-atomic-updates + generator = await createTypingsGeneratorAsync( + taskSession, + heftConfiguration, + staticAssetGeneratorOptions + ); + } + + if (generator === false) { + return; + } + + await generator.runIncrementalAsync(runOptions); + } + + taskSession.hooks.run.tapPromise(PLUGIN_NAME, createAndRunGeneratorAsync); + taskSession.hooks.runIncremental.tapPromise(PLUGIN_NAME, createAndRunGeneratorAsync); + } +} diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/ImageTypingsGeneratorHeftPlugin.ts b/heft-plugins/heft-image-typings-generator-plugin/src/ImageTypingsGeneratorHeftPlugin.ts deleted file mode 100644 index 9adb0c0c96a..00000000000 --- a/heft-plugins/heft-image-typings-generator-plugin/src/ImageTypingsGeneratorHeftPlugin.ts +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -// See LICENSE in the project root for license information. - -import path from 'node:path'; - -import type { IHeftPlugin, IHeftTaskSession, HeftConfiguration } from '@rushstack/heft'; -import { TypingsGenerator } from '@rushstack/typings-generator'; - -export type FileExtension = `.${string}`; - -const PLUGIN_NAME: string = 'image-typings-generator'; -const DEFAULT_FILE_EXTENSIONS: FileExtension[] = ['.png', '.jpg', '.jpeg', '.gif', '.svg']; - -export interface IImageTypingsGeneratorHeftPluginOptions { - fileExtensions?: FileExtension[]; - generatedTsFolder: string; - srcFolder?: string; -} - -export default class ImageTypingsGeneratorPlugin - implements IHeftPlugin -{ - public apply( - heftSession: IHeftTaskSession, - heftConfiguration: HeftConfiguration, - options: IImageTypingsGeneratorHeftPluginOptions - ): void { - const { - logger: { terminal } - } = heftSession; - const { buildFolderPath } = heftConfiguration; - const { fileExtensions = DEFAULT_FILE_EXTENSIONS, generatedTsFolder, srcFolder = 'src' } = options; - - const typingsGenerator: TypingsGenerator = new TypingsGenerator({ - fileExtensions, - // eslint-disable-next-line @typescript-eslint/naming-convention - readFile: async (filePath: string) => '', - srcFolder: path.resolve(buildFolderPath, srcFolder), - generatedTsFolder: path.resolve(buildFolderPath, generatedTsFolder), - parseAndGenerateTypings: () => 'declare const imageUrl: string;\nexport default imageUrl;', - terminal - }); - - heftSession.hooks.run.tapPromise(PLUGIN_NAME, async () => { - await typingsGenerator.generateTypingsAsync(); - }); - } -} diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/StaticAssetTypingsGenerator.ts b/heft-plugins/heft-image-typings-generator-plugin/src/StaticAssetTypingsGenerator.ts new file mode 100644 index 00000000000..ac010770b5e --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/src/StaticAssetTypingsGenerator.ts @@ -0,0 +1,260 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { + HeftConfiguration, + IHeftTaskRunHookOptions, + IHeftTaskRunIncrementalHookOptions, + IHeftTaskSession, + IWatchedFileState +} from '@rushstack/heft'; +import { TypingsGenerator } from '@rushstack/typings-generator'; +import { FileSystem, Sort } from '@rushstack/node-core-library'; + +import type { IStaticAssetTypingsConfigurationJson, StaticAssetConfigurationFileLoader } from './types'; + +const DECLARATION: string = `/** + * @public + */ +declare const content: string; +export default content; +`; + +const PLUGIN_VERSION: number = 1; + +/** + * Options for constructing a static asset typings generator + */ +export interface IStaticAssetGeneratorOptions { + /** + * A getter for the loader for the riggable config file in the project. + */ + tryGetConfigAsync: StaticAssetConfigurationFileLoader; + /** + * The path to the build folder, normalized to use forward slashes as the directory separator. + */ + slashNormalizedBuildFolderPath: string; + /** + * + * @param relativePath - The relative path of the file to get additional output files for. + * @returns An array of output file names. + */ + getAdditionalOutputFiles?: (relativePath: string) => string[]; + /** + * + * @param relativePath - The relative path of the file being processed. + * @param filePath - The absolute path of the file being processed. + * @param oldVersion - The old version of the file, if any. + * @returns The new version of the file, if emit should occur. + */ + getVersionAndEmitOutputFilesAsync: ( + relativePath: string, + filePath: string, + oldVersion: string | undefined + ) => Promise; +} + +export type IRunGeneratorOptions = IHeftTaskRunHookOptions & + Partial>; + +export interface IStaticAssetTypingsGenerator { + /** + * Runs this generator in incremental mode. + * + * @param runOptions - The task run hook options from Heft + * @returns A promise that resolves when the generator has finished processing. + */ + runIncrementalAsync: (runOptions: IRunGeneratorOptions) => Promise; +} + +interface IStaticAssetTypingsConfiguration extends IStaticAssetTypingsConfigurationJson { + srcFolder: string; + generatedTsFolder: string; +} + +interface IStaticAssetTypingsBuildInfoFile { + fileVersions: [string, string][]; + pluginVersion: number; +} + +/** + * Constructs a typings generator for processing static assets + * + * @param taskSession - The Heft task session + * @param heftConfiguration - The Heft configuration + * @param options - Options for the generator + * @returns + */ +export async function createTypingsGeneratorAsync( + taskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, + options: IStaticAssetGeneratorOptions +): Promise { + const { tryGetConfigAsync, slashNormalizedBuildFolderPath } = options; + + const { terminal } = taskSession.logger; + + const configurationJson: IStaticAssetTypingsConfigurationJson | undefined = await tryGetConfigAsync( + terminal, + slashNormalizedBuildFolderPath, + heftConfiguration.rigConfig + ); + + if (!configurationJson) { + return false; + } + + const secondaryGeneratedTsFolders: string[] | undefined = + configurationJson.secondaryGeneratedTsFolders?.map( + (folder) => `${slashNormalizedBuildFolderPath}/${folder}` + ); + + const { generatedTsFolder = 'temp/static-asset-ts', sourceFolderPath = 'src' } = configurationJson; + + const configuration: IStaticAssetTypingsConfiguration = { + ...configurationJson, + srcFolder: `${slashNormalizedBuildFolderPath}/${sourceFolderPath}`, + generatedTsFolder: `${slashNormalizedBuildFolderPath}/${generatedTsFolder}`, + secondaryGeneratedTsFolders + }; + + const { getAdditionalOutputFiles, getVersionAndEmitOutputFilesAsync } = options; + + const fileVersions: Map = new Map(); + + const typingsGenerator: TypingsGenerator = new TypingsGenerator({ + ...configuration, + terminal, + // eslint-disable-next-line @typescript-eslint/naming-convention + parseAndGenerateTypings: async ( + fileContents: boolean, + filePath: string, + relativePath: string + ): Promise => { + const oldFileVersion: string | undefined = fileVersions.get(relativePath); + const fileVersion: string | undefined = await getVersionAndEmitOutputFilesAsync( + filePath, + relativePath, + oldFileVersion + ); + if (fileVersion === undefined) { + return; + } + + fileVersions.set(relativePath, fileVersion); + if (oldFileVersion) { + // Since DECLARATION is constant, no point re-emitting the declarations just because the input content changed. + return; + } + + return DECLARATION; + }, + readFile: (filePath: string, relativePath: string): boolean => { + return false; + }, + getAdditionalOutputFiles + }); + + const cacheFilePath: string = `${taskSession.tempFolderPath}/static-assets.json`; + try { + const cacheFileContent: string = await FileSystem.readFileAsync(cacheFilePath); + const oldCacheFile: IStaticAssetTypingsBuildInfoFile = JSON.parse(cacheFileContent); + if (oldCacheFile.pluginVersion === PLUGIN_VERSION) { + for (const [relativePath, version] of oldCacheFile.fileVersions) { + fileVersions.set(relativePath, version); + } + } + } catch (e) { + terminal.writeVerboseLine(`Failed to read cache file: ${e}`); + } + + return { + async runIncrementalAsync(runOptions: IRunGeneratorOptions): Promise { + await runTypingsGeneratorIncrementalAsync( + taskSession, + typingsGenerator, + cacheFilePath, + fileVersions, + runOptions + ); + } + }; +} + +/** + * Invokes the specified typings generator on any files changed since the last invocation. + * If the cache file has been deleted (e.g. via a `--clean` run), will process all files. + * + * @param taskSession - The Heft task session. + * @param typingsGenerator - The typings generator to invoke. + * @param cacheFilePath - The path to the file that will contain the last build file version metadata. + * @param fileVersions - The map of current file versions. + * @param heftRunOptions - The task options from Heft. + * @returns A promise that resolves when the typings generator has finished processing. + */ +async function runTypingsGeneratorIncrementalAsync( + taskSession: IHeftTaskSession, + typingsGenerator: TypingsGenerator, + cacheFilePath: string, + fileVersions: Map, + heftRunOptions: IRunGeneratorOptions +): Promise { + const { terminal } = taskSession.logger; + + const originalFileVersions: ReadonlyMap = new Map(fileVersions); + + // If we have the incremental options, use them to determine which files to process. + // Otherwise, process all files. The typings generator also provides the file paths + // as relative paths from the sourceFolderPath. + let changedRelativeFilePaths: string[] | undefined; + const { watchGlobAsync } = heftRunOptions as IHeftTaskRunIncrementalHookOptions; + if (watchGlobAsync) { + changedRelativeFilePaths = []; + const relativeFilePaths: Map = await watchGlobAsync( + typingsGenerator.inputFileGlob, + { + cwd: typingsGenerator.sourceFolderPath, + ignore: Array.from(typingsGenerator.ignoredFileGlobs), + absolute: false + } + ); + for (const [relativeFilePath, { changed }] of relativeFilePaths) { + if (changed) { + changedRelativeFilePaths.push(relativeFilePath); + } + } + + if (changedRelativeFilePaths.length === 0) { + return; + } + } + + terminal.writeLine('Processing static assets...'); + await typingsGenerator.generateTypingsAsync(changedRelativeFilePaths); + + if (hasChanges(fileVersions, originalFileVersions)) { + const fileVersionsArray: [string, string][] = Array.from(fileVersions); + Sort.sortBy(fileVersionsArray, ([relativePath]) => relativePath); + + const buildFile: IStaticAssetTypingsBuildInfoFile = { + fileVersions: fileVersionsArray, + pluginVersion: PLUGIN_VERSION + }; + await FileSystem.writeFileAsync(cacheFilePath, JSON.stringify(buildFile), { ensureFolderExists: true }); + } + terminal.writeLine('Finished processing static assets.'); +} + +function hasChanges(current: ReadonlyMap, old: ReadonlyMap): boolean { + if (current.size !== old.size) { + return true; + } + + for (const [key, value] of current) { + if (old.get(key) !== value) { + return true; + } + } + + return false; +} diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/TextAssetsPlugin.ts b/heft-plugins/heft-image-typings-generator-plugin/src/TextAssetsPlugin.ts new file mode 100644 index 00000000000..32ac1db5c98 --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/src/TextAssetsPlugin.ts @@ -0,0 +1,138 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { createHash } from 'node:crypto'; + +import type { HeftConfiguration, IHeftTaskSession, IHeftTaskPlugin } from '@rushstack/heft'; +import { FileSystem, Path } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; + +import { + createTypingsGeneratorAsync, + type IRunGeneratorOptions, + type IStaticAssetGeneratorOptions, + type IStaticAssetTypingsGenerator +} from './StaticAssetTypingsGenerator'; +import { getConfigFromConfigFileAsync } from './getConfigFromConfigFileAsync'; +import type { IStaticAssetTypingsConfigurationJson } from './types'; + +const PLUGIN_NAME: 'text-assets-plugin' = 'text-assets-plugin'; + +export interface ITextAssetsPluginOptions { + cjsOutputFolders: string[]; + esmOutputFolders: string[]; + configFileName?: string; +} + +export default class TextAssetsPlugin implements IHeftTaskPlugin { + /** + * Generate typings for text files before TypeScript compilation. + */ + public apply( + taskSession: IHeftTaskSession, + heftConfiguration: HeftConfiguration, + options: ITextAssetsPluginOptions + ): void { + const slashNormalizedBuildFolderPath: string = Path.convertToSlashes(heftConfiguration.buildFolderPath); + + const cjsOutputFolders: string[] = options.cjsOutputFolders.map( + (jsPath) => `${slashNormalizedBuildFolderPath}/${jsPath}` + ); + const esmOutputFolders: string[] = options.esmOutputFolders.map( + (jsPath) => `${slashNormalizedBuildFolderPath}/${jsPath}` + ); + const jsOutputFolders: string[] = [...cjsOutputFolders, ...esmOutputFolders]; + + const { configFileName = 'text-assets.json' } = options; + + function getAdditionalOutputFiles(relativePath: string): string[] { + return jsOutputFolders.map((folder) => `${folder}/${relativePath}.js`); + } + + async function tryGetConfigAsync( + terminal: ITerminal, + buildFolderPath: string, + rigConfig: HeftConfiguration['rigConfig'] + ): Promise { + return getConfigFromConfigFileAsync(configFileName, terminal, buildFolderPath, rigConfig); + } + + async function getVersionAndEmitOutputFilesAsync( + filePath: string, + relativePath: string, + oldVersion: string | undefined + ): Promise { + const fileContents: Buffer = await FileSystem.readFileToBufferAsync(filePath); + const fileVersion: string = createHash('sha1').update(fileContents).digest('base64'); + if (fileVersion === oldVersion) { + return; + } + + const stringFileContents: string = fileContents.toString('utf8'); + + const stringifiedContents: string = JSON.stringify(stringFileContents); + + const outputs: Map = new Map(); + if (cjsOutputFolders.length) { + const outputSource: string = [ + `"use strict"`, + `Object.defineProperty(exports, "__esModule", { value: true });`, + `var content = ${stringifiedContents};`, + `exports.default = content;` + ].join('\n'); + const outputBuffer: Buffer = Buffer.from(outputSource); + for (const folder of cjsOutputFolders) { + outputs.set(`${folder}/${relativePath}.js`, outputBuffer); + } + } + + if (esmOutputFolders.length) { + const outputSource: string = [ + `const content = ${stringifiedContents};`, + `export default content;` + ].join('\n'); + const outputBuffer: Buffer = Buffer.from(outputSource); + for (const folder of esmOutputFolders) { + outputs.set(`${folder}/${relativePath}.js`, outputBuffer); + } + } + + await Promise.all( + Array.from(outputs, ([outputPath, outputBuffer]) => + FileSystem.writeFileAsync(outputPath, outputBuffer, { ensureFolderExists: true }) + ) + ); + + return fileVersion; + } + + const staticAssetGeneratorOptions: IStaticAssetGeneratorOptions = { + tryGetConfigAsync, + slashNormalizedBuildFolderPath, + getAdditionalOutputFiles, + getVersionAndEmitOutputFilesAsync + }; + + let generator: IStaticAssetTypingsGenerator | undefined | false; + + async function createAndRunGeneratorAsync(runOptions: IRunGeneratorOptions): Promise { + if (generator === undefined) { + // eslint-disable-next-line require-atomic-updates + generator = await createTypingsGeneratorAsync( + taskSession, + heftConfiguration, + staticAssetGeneratorOptions + ); + } + + if (generator === false) { + return; + } + + await generator.runIncrementalAsync(runOptions); + } + + taskSession.hooks.run.tapPromise(PLUGIN_NAME, createAndRunGeneratorAsync); + taskSession.hooks.runIncremental.tapPromise(PLUGIN_NAME, createAndRunGeneratorAsync); + } +} diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/getConfigFromConfigFileAsync.ts b/heft-plugins/heft-image-typings-generator-plugin/src/getConfigFromConfigFileAsync.ts new file mode 100644 index 00000000000..44a20e897e4 --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/src/getConfigFromConfigFileAsync.ts @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { HeftConfiguration } from '@rushstack/heft'; +import { ConfigurationFile, InheritanceType } from '@rushstack/heft-config-file'; +import type { ITerminal } from '@rushstack/terminal'; + +import type { IStaticAssetTypingsConfigurationJson } from './types'; +import staticAssetSchema from './schemas/static-asset-typings.schema.json'; + +const configurationFileLoaderByFileName: Map< + string, + ConfigurationFile +> = new Map(); + +function createConfigurationFileLoader( + configFileName: string +): ConfigurationFile { + return new ConfigurationFile({ + jsonSchemaObject: staticAssetSchema, + projectRelativeFilePath: `config/${configFileName}`, + propertyInheritance: { + fileExtensions: { + inheritanceType: InheritanceType.append + } + } + }); +} + +export function getConfigFromConfigFileAsync( + configFileName: string, + terminal: ITerminal, + slashNormalizedBuildFolderPath: string, + rigConfig: HeftConfiguration['rigConfig'] +): Promise { + let configurationFileLoader: ConfigurationFile | undefined = + configurationFileLoaderByFileName.get(configFileName); + if (!configurationFileLoader) { + configurationFileLoader = createConfigurationFileLoader(configFileName); + configurationFileLoaderByFileName.set(configFileName, configurationFileLoader); + } + + return configurationFileLoader.tryLoadConfigurationFileForProjectAsync( + terminal, + slashNormalizedBuildFolderPath, + rigConfig + ); +} diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/schemas/binary-assets-options.schema.json b/heft-plugins/heft-image-typings-generator-plugin/src/schemas/binary-assets-options.schema.json new file mode 100644 index 00000000000..64218a34e98 --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/src/schemas/binary-assets-options.schema.json @@ -0,0 +1,61 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "required": ["configType", "config"], + "properties": { + "configType": { + "type": "string", + "enum": ["inline"] + }, + "config": { + "required": ["fileExtensions"], + "type": "object", + "additionalProperties": false, + "properties": { + "$schema": { + "type": "string" + }, + "fileExtensions": { + "type": "array", + "items": { + "pattern": "\\.[^\\\\/]+$", + "type": "string" + } + }, + "generatedTsFolder": { + "type": "string" + }, + "secondaryGeneratedTsFolders": { + "type": "array", + "items": { + "type": "string" + } + }, + "sourceFolderPath": { + "type": "string" + } + } + } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": ["configType", "configFileName"], + "properties": { + "configType": { + "type": "string", + "enum": ["file"] + }, + "configFileName": { + "type": "string", + "pattern": "^[^\\\\\\/]+\\.json$" + } + } + } + ] +} diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/schemas/image-typings-generator-plugin-options.schema.json b/heft-plugins/heft-image-typings-generator-plugin/src/schemas/image-typings-generator-plugin-options.schema.json deleted file mode 100644 index 7f0ea4f9ccd..00000000000 --- a/heft-plugins/heft-image-typings-generator-plugin/src/schemas/image-typings-generator-plugin-options.schema.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - - "additionalProperties": false, - - "required": ["generatedTsFolder"], - "properties": { - "fileExtensions": { - "description": "File extensions to generate typings for, e.g. \".png\", \".jpg\", \".svg\". Defaults to [\".png\", \".jpg\", \".jpeg\", \".gif\", \".svg\"] if not provided.", - "type": "array", - "items": { - "type": "string", - "pattern": "\\..+" - } - }, - "generatedTsFolder": { - "description": "The output folder where generated .d.ts typings will be written.", - "type": "string" - }, - "srcFolder": { - "description": "The source folder to scan for image files. Defaults to \"src\".", - "type": "string" - } - } -} diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/schemas/static-asset-typings.schema.json b/heft-plugins/heft-image-typings-generator-plugin/src/schemas/static-asset-typings.schema.json new file mode 100644 index 00000000000..05c91a28aba --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/src/schemas/static-asset-typings.schema.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "additionalProperties": false, + "required": ["fileExtensions"], + "properties": { + "$schema": { + "type": "string" + }, + "fileExtensions": { + "type": "array", + "items": { + "pattern": "\\.[^\\\\/]+$", + "type": "string" + } + }, + "generatedTsFolder": { + "type": "string" + }, + "secondaryGeneratedTsFolders": { + "type": "array", + "items": { + "type": "string" + } + }, + "sourceFolderPath": { + "type": "string" + } + } +} diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/schemas/text-assets-options.schema.json b/heft-plugins/heft-image-typings-generator-plugin/src/schemas/text-assets-options.schema.json new file mode 100644 index 00000000000..b1c7affd539 --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/src/schemas/text-assets-options.schema.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "additionalProperties": false, + "required": ["cjsOutputFolders"], + "properties": { + "$schema": { + "type": "string" + }, + "cjsOutputFolders": { + "type": "array", + "items": { + "pattern": "^[^\\\\]+$", + "type": "string" + } + }, + "esmOutputFolders": { + "type": "array", + "items": { + "pattern": "^[^\\\\]+$", + "type": "string" + } + }, + "configFileName": { + "type": "string", + "pattern": "^[^\\\\\\/]+\\.json$" + } + } +} diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/types.ts b/heft-plugins/heft-image-typings-generator-plugin/src/types.ts new file mode 100644 index 00000000000..eefbbd80379 --- /dev/null +++ b/heft-plugins/heft-image-typings-generator-plugin/src/types.ts @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { HeftConfiguration } from '@rushstack/heft'; +import type { ITerminal } from '@rushstack/terminal'; + +export interface IStaticAssetTypingsConfigurationJson { + fileExtensions: string[]; + generatedTsFolder?: string; + secondaryGeneratedTsFolders?: string[]; + sourceFolderPath?: string; +} + +export type StaticAssetConfigurationFileLoader = ( + terminal: ITerminal, + slashNormalizedBuildFolderPath: string, + rigConfig: HeftConfiguration['rigConfig'] +) => Promise; From d302b45c8e4c98c83eff2d6934e4ff02ce1642a2 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Mon, 23 Feb 2026 14:29:34 -0800 Subject: [PATCH 05/17] fixup! Rewrite the plugin to handle general static assets. --- .../config/heft.json | 11 ++ .../config/jest.config.json | 4 + .../config/rush-project.json | 2 +- .../config/text-assets.json | 4 + .../etc/heft-node-everything-test.api.md | 3 + .../heft-node-everything-test/package.json | 1 + .../src/exampleTemplate.html | 9 ++ .../heft-node-everything-test/src/index.ts | 8 + .../src/test/ExampleTest.test.ts | 8 + .../heft-node-everything-test/tsconfig.json | 1 + .../config/heft.json | 2 +- .../heft-rspack-everything-test/package.json | 2 +- .../config/heft.json | 2 +- .../package.json | 2 +- .../config/heft.json | 2 +- .../package.json | 2 +- .../config/heft.json | 2 +- .../package.json | 2 +- .../main_2026-02-23-21-40.json | 10 -- .../main_2026-02-23-21-40.json | 10 ++ ...ngs-generator-plugin_2026-02-23-22-01.json | 2 +- .../rush/nonbrowser-approved-packages.json | 2 +- .../config/subspaces/default/pnpm-lock.yaml | 83 +++++----- .../README.md | 65 -------- .../.npmignore | 0 .../LICENSE | 2 +- .../README.md | 145 ++++++++++++++++++ .../config/heft.json | 0 .../config/jest.config.json | 0 .../config/rig.json | 0 .../eslint.config.js | 0 .../heft-plugin.json | 0 .../package.json | 6 +- .../src/BinaryAssetsPlugin.ts | 0 .../src/StaticAssetTypingsGenerator.ts | 0 .../src/TextAssetsPlugin.ts | 0 .../src/getConfigFromConfigFileAsync.ts | 0 .../schemas/binary-assets-options.schema.json | 0 .../schemas/static-asset-typings.schema.json | 0 .../schemas/text-assets-options.schema.json | 0 .../src/types.ts | 0 .../tsconfig.json | 0 rigs/heft-web-rig/package.json | 2 +- .../profiles/app/config/heft.json | 2 +- .../profiles/library/config/heft.json | 2 +- rush.json | 12 +- 46 files changed, 271 insertions(+), 139 deletions(-) create mode 100644 build-tests/heft-node-everything-test/config/text-assets.json create mode 100644 build-tests/heft-node-everything-test/src/exampleTemplate.html delete mode 100644 common/changes/@rushstack/heft-image-typings-generator-plugin/main_2026-02-23-21-40.json create mode 100644 common/changes/@rushstack/heft-static-asset-typings-plugin/main_2026-02-23-21-40.json delete mode 100644 heft-plugins/heft-image-typings-generator-plugin/README.md rename heft-plugins/{heft-image-typings-generator-plugin => heft-static-asset-typings-plugin}/.npmignore (100%) rename heft-plugins/{heft-image-typings-generator-plugin => heft-static-asset-typings-plugin}/LICENSE (95%) create mode 100644 heft-plugins/heft-static-asset-typings-plugin/README.md rename heft-plugins/{heft-image-typings-generator-plugin => heft-static-asset-typings-plugin}/config/heft.json (100%) rename heft-plugins/{heft-image-typings-generator-plugin => heft-static-asset-typings-plugin}/config/jest.config.json (100%) rename heft-plugins/{heft-image-typings-generator-plugin => heft-static-asset-typings-plugin}/config/rig.json (100%) rename heft-plugins/{heft-image-typings-generator-plugin => heft-static-asset-typings-plugin}/eslint.config.js (100%) rename heft-plugins/{heft-image-typings-generator-plugin => heft-static-asset-typings-plugin}/heft-plugin.json (100%) rename heft-plugins/{heft-image-typings-generator-plugin => heft-static-asset-typings-plugin}/package.json (82%) rename heft-plugins/{heft-image-typings-generator-plugin => heft-static-asset-typings-plugin}/src/BinaryAssetsPlugin.ts (100%) rename heft-plugins/{heft-image-typings-generator-plugin => heft-static-asset-typings-plugin}/src/StaticAssetTypingsGenerator.ts (100%) rename heft-plugins/{heft-image-typings-generator-plugin => heft-static-asset-typings-plugin}/src/TextAssetsPlugin.ts (100%) rename heft-plugins/{heft-image-typings-generator-plugin => heft-static-asset-typings-plugin}/src/getConfigFromConfigFileAsync.ts (100%) rename heft-plugins/{heft-image-typings-generator-plugin => heft-static-asset-typings-plugin}/src/schemas/binary-assets-options.schema.json (100%) rename heft-plugins/{heft-image-typings-generator-plugin => heft-static-asset-typings-plugin}/src/schemas/static-asset-typings.schema.json (100%) rename heft-plugins/{heft-image-typings-generator-plugin => heft-static-asset-typings-plugin}/src/schemas/text-assets-options.schema.json (100%) rename heft-plugins/{heft-image-typings-generator-plugin => heft-static-asset-typings-plugin}/src/types.ts (100%) rename heft-plugins/{heft-image-typings-generator-plugin => heft-static-asset-typings-plugin}/tsconfig.json (100%) diff --git a/build-tests/heft-node-everything-test/config/heft.json b/build-tests/heft-node-everything-test/config/heft.json index 717d3c6576c..63e653cf8ff 100644 --- a/build-tests/heft-node-everything-test/config/heft.json +++ b/build-tests/heft-node-everything-test/config/heft.json @@ -16,7 +16,18 @@ "cleanFiles": [{ "includeGlobs": ["dist", "lib-commonjs", "lib", "lib-esm", "lib-esnext", "lib-umd"] }], "tasksByName": { + "text-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "text-assets-plugin", + "options": { + "cjsOutputFolders": ["lib-commonjs"], + "esmOutputFolders": ["lib-esm"] + } + } + }, "typescript": { + "taskDependencies": ["text-typings"], "taskPlugin": { "pluginPackage": "@rushstack/heft-typescript-plugin" } diff --git a/build-tests/heft-node-everything-test/config/jest.config.json b/build-tests/heft-node-everything-test/config/jest.config.json index c0687c6d488..8003417cece 100644 --- a/build-tests/heft-node-everything-test/config/jest.config.json +++ b/build-tests/heft-node-everything-test/config/jest.config.json @@ -1,6 +1,10 @@ { "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", + // The project outputs to lib-commonjs, not lib + "roots": ["/lib-commonjs"], + "testMatch": ["/lib-commonjs/**/*.test.{cjs,js}"], + // Enable code coverage for Jest "collectCoverage": true, "coverageDirectory": "/coverage", diff --git a/build-tests/heft-node-everything-test/config/rush-project.json b/build-tests/heft-node-everything-test/config/rush-project.json index 40a0d93f857..50339f80875 100644 --- a/build-tests/heft-node-everything-test/config/rush-project.json +++ b/build-tests/heft-node-everything-test/config/rush-project.json @@ -4,7 +4,7 @@ "operationSettings": [ { "operationName": "_phase:build", - "outputFolderNames": ["dist", "lib-commonjs", "lib-esm", "lib-umd", "lib-dts"] + "outputFolderNames": ["dist", "lib-commonjs", "lib-esm", "lib-umd", "lib-dts", "temp/text-typings"] }, { "operationName": "_phase:test", diff --git a/build-tests/heft-node-everything-test/config/text-assets.json b/build-tests/heft-node-everything-test/config/text-assets.json new file mode 100644 index 00000000000..e35e877e016 --- /dev/null +++ b/build-tests/heft-node-everything-test/config/text-assets.json @@ -0,0 +1,4 @@ +{ + "fileExtensions": [".html"], + "generatedTsFolder": "temp/text-typings" +} diff --git a/build-tests/heft-node-everything-test/etc/heft-node-everything-test.api.md b/build-tests/heft-node-everything-test/etc/heft-node-everything-test.api.md index 73e6e6a967e..906e342ef52 100644 --- a/build-tests/heft-node-everything-test/etc/heft-node-everything-test.api.md +++ b/build-tests/heft-node-everything-test/etc/heft-node-everything-test.api.md @@ -4,6 +4,9 @@ ```ts +// @public +export const templateContent: string; + // @public (undocumented) export class TestClass { } diff --git a/build-tests/heft-node-everything-test/package.json b/build-tests/heft-node-everything-test/package.json index bd60e9a4a55..fcf92f04c29 100644 --- a/build-tests/heft-node-everything-test/package.json +++ b/build-tests/heft-node-everything-test/package.json @@ -16,6 +16,7 @@ "@microsoft/api-extractor": "workspace:*", "@rushstack/heft": "workspace:*", "@rushstack/heft-api-extractor-plugin": "workspace:*", + "@rushstack/heft-static-asset-typings-plugin": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", diff --git a/build-tests/heft-node-everything-test/src/exampleTemplate.html b/build-tests/heft-node-everything-test/src/exampleTemplate.html new file mode 100644 index 00000000000..bba457469cc --- /dev/null +++ b/build-tests/heft-node-everything-test/src/exampleTemplate.html @@ -0,0 +1,9 @@ + + + + Example Template + + +

Hello, world!

+ + diff --git a/build-tests/heft-node-everything-test/src/index.ts b/build-tests/heft-node-everything-test/src/index.ts index 659610ef84f..22b50eb31b1 100644 --- a/build-tests/heft-node-everything-test/src/index.ts +++ b/build-tests/heft-node-everything-test/src/index.ts @@ -1,6 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import exampleTemplate from './exampleTemplate.html'; + +/** + * The content of exampleTemplate.html, imported as a text asset. + * @public + */ +export const templateContent: string = exampleTemplate; + /** * @public */ diff --git a/build-tests/heft-node-everything-test/src/test/ExampleTest.test.ts b/build-tests/heft-node-everything-test/src/test/ExampleTest.test.ts index ccae242d321..26fa4ae4bb1 100644 --- a/build-tests/heft-node-everything-test/src/test/ExampleTest.test.ts +++ b/build-tests/heft-node-everything-test/src/test/ExampleTest.test.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import { templateContent } from '../index'; + interface IInterface { element: string; } @@ -20,4 +22,10 @@ describe('Example Test', () => { }; expect(interfaceInstance).toBeTruthy(); }); + + it('Correctly imports text assets', () => { + expect(typeof templateContent).toBe('string'); + expect(templateContent).toContain('Example Template'); + expect(templateContent).toContain('Hello, world!'); + }); }); diff --git a/build-tests/heft-node-everything-test/tsconfig.json b/build-tests/heft-node-everything-test/tsconfig.json index d08637a6a20..f5ce1bd1dcc 100644 --- a/build-tests/heft-node-everything-test/tsconfig.json +++ b/build-tests/heft-node-everything-test/tsconfig.json @@ -5,6 +5,7 @@ "outDir": "lib-commonjs", "declarationDir": "lib-dts", "rootDir": "src", + "rootDirs": ["src", "temp/text-typings"], "forceConsistentCasingInFileNames": true, "jsx": "react", diff --git a/build-tests/heft-rspack-everything-test/config/heft.json b/build-tests/heft-rspack-everything-test/config/heft.json index 9c73eefd33f..290c43699ee 100644 --- a/build-tests/heft-rspack-everything-test/config/heft.json +++ b/build-tests/heft-rspack-everything-test/config/heft.json @@ -12,7 +12,7 @@ "tasksByName": { "image-typings": { "taskPlugin": { - "pluginPackage": "@rushstack/heft-image-typings-generator-plugin", + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", "options": { "fileExtensions": [".png"], "generatedTsFolder": "temp/image-typings" diff --git a/build-tests/heft-rspack-everything-test/package.json b/build-tests/heft-rspack-everything-test/package.json index aa98a6594e4..9ef1796e971 100644 --- a/build-tests/heft-rspack-everything-test/package.json +++ b/build-tests/heft-rspack-everything-test/package.json @@ -12,7 +12,7 @@ }, "devDependencies": { "@rushstack/heft-dev-cert-plugin": "workspace:*", - "@rushstack/heft-image-typings-generator-plugin": "workspace:*", + "@rushstack/heft-static-asset-typings-plugin": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", diff --git a/build-tests/heft-typescript-composite-test/config/heft.json b/build-tests/heft-typescript-composite-test/config/heft.json index a49cba975a2..95d95bc6bab 100644 --- a/build-tests/heft-typescript-composite-test/config/heft.json +++ b/build-tests/heft-typescript-composite-test/config/heft.json @@ -12,7 +12,7 @@ "tasksByName": { "image-typings": { "taskPlugin": { - "pluginPackage": "@rushstack/heft-image-typings-generator-plugin", + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", "options": { "fileExtensions": [".png"], "generatedTsFolder": "temp/image-typings" diff --git a/build-tests/heft-typescript-composite-test/package.json b/build-tests/heft-typescript-composite-test/package.json index cf8f5cf66db..584f983703b 100644 --- a/build-tests/heft-typescript-composite-test/package.json +++ b/build-tests/heft-typescript-composite-test/package.json @@ -11,7 +11,7 @@ }, "devDependencies": { "@rushstack/heft": "workspace:*", - "@rushstack/heft-image-typings-generator-plugin": "workspace:*", + "@rushstack/heft-static-asset-typings-plugin": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", diff --git a/build-tests/heft-webpack4-everything-test/config/heft.json b/build-tests/heft-webpack4-everything-test/config/heft.json index 370b30f73ae..7d03ef3c7e9 100644 --- a/build-tests/heft-webpack4-everything-test/config/heft.json +++ b/build-tests/heft-webpack4-everything-test/config/heft.json @@ -12,7 +12,7 @@ "tasksByName": { "image-typings": { "taskPlugin": { - "pluginPackage": "@rushstack/heft-image-typings-generator-plugin", + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", "options": { "fileExtensions": [".png"], "generatedTsFolder": "temp/image-typings" diff --git a/build-tests/heft-webpack4-everything-test/package.json b/build-tests/heft-webpack4-everything-test/package.json index cec1a663956..0742ce3648d 100644 --- a/build-tests/heft-webpack4-everything-test/package.json +++ b/build-tests/heft-webpack4-everything-test/package.json @@ -11,7 +11,7 @@ }, "devDependencies": { "@rushstack/heft-dev-cert-plugin": "workspace:*", - "@rushstack/heft-image-typings-generator-plugin": "workspace:*", + "@rushstack/heft-static-asset-typings-plugin": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", diff --git a/build-tests/heft-webpack5-everything-test/config/heft.json b/build-tests/heft-webpack5-everything-test/config/heft.json index 703d33b5a60..7dd78bf182a 100644 --- a/build-tests/heft-webpack5-everything-test/config/heft.json +++ b/build-tests/heft-webpack5-everything-test/config/heft.json @@ -12,7 +12,7 @@ "tasksByName": { "image-typings": { "taskPlugin": { - "pluginPackage": "@rushstack/heft-image-typings-generator-plugin", + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", "options": { "fileExtensions": [".png"], "generatedTsFolder": "temp/image-typings" diff --git a/build-tests/heft-webpack5-everything-test/package.json b/build-tests/heft-webpack5-everything-test/package.json index fae41f8ef3c..3e891b7778c 100644 --- a/build-tests/heft-webpack5-everything-test/package.json +++ b/build-tests/heft-webpack5-everything-test/package.json @@ -13,7 +13,7 @@ }, "devDependencies": { "@rushstack/heft-dev-cert-plugin": "workspace:*", - "@rushstack/heft-image-typings-generator-plugin": "workspace:*", + "@rushstack/heft-static-asset-typings-plugin": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", diff --git a/common/changes/@rushstack/heft-image-typings-generator-plugin/main_2026-02-23-21-40.json b/common/changes/@rushstack/heft-image-typings-generator-plugin/main_2026-02-23-21-40.json deleted file mode 100644 index aaaf9cf5dee..00000000000 --- a/common/changes/@rushstack/heft-image-typings-generator-plugin/main_2026-02-23-21-40.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@rushstack/heft-image-typings-generator-plugin", - "comment": "Initial release.", - "type": "minor" - } - ], - "packageName": "@rushstack/heft-image-typings-generator-plugin" -} \ No newline at end of file diff --git a/common/changes/@rushstack/heft-static-asset-typings-plugin/main_2026-02-23-21-40.json b/common/changes/@rushstack/heft-static-asset-typings-plugin/main_2026-02-23-21-40.json new file mode 100644 index 00000000000..5b137c4bf1d --- /dev/null +++ b/common/changes/@rushstack/heft-static-asset-typings-plugin/main_2026-02-23-21-40.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/heft-static-asset-typings-plugin", + "comment": "Initial release.", + "type": "minor" + } + ], + "packageName": "@rushstack/heft-static-asset-typings-plugin" +} \ No newline at end of file diff --git a/common/changes/@rushstack/heft-web-rig/heft-image-typings-generator-plugin_2026-02-23-22-01.json b/common/changes/@rushstack/heft-web-rig/heft-image-typings-generator-plugin_2026-02-23-22-01.json index e3c919aa87d..51e93336e7b 100644 --- a/common/changes/@rushstack/heft-web-rig/heft-image-typings-generator-plugin_2026-02-23-22-01.json +++ b/common/changes/@rushstack/heft-web-rig/heft-image-typings-generator-plugin_2026-02-23-22-01.json @@ -2,7 +2,7 @@ "changes": [ { "packageName": "@rushstack/heft-web-rig", - "comment": "Add `@rushstack/heft-image-typings-generator-plugin` to the `app` and `library` profiles, generating `.d.ts` typings for image files (`.png`, `.jpg`, `.jpeg`, `.gif`, `.svg`, `.ico`, `.webp`, `.avif`). This enables type-safe image imports in TypeScript without needing `allowArbitraryExtensions` or `require()` calls. Also add `.webp` and `.avif` to the webpack asset resource rule.", + "comment": "Add `@rushstack/heft-static-asset-typings-plugin` to the `app` and `library` profiles, generating `.d.ts` typings for static asset files (images and text). This enables type-safe asset imports in TypeScript without needing `allowArbitraryExtensions` or `require()` calls. Also add `.webp` and `.avif` to the webpack asset resource rule.", "type": "minor" } ], diff --git a/common/config/rush/nonbrowser-approved-packages.json b/common/config/rush/nonbrowser-approved-packages.json index a85e837ddad..4eb4bb89b8d 100644 --- a/common/config/rush/nonbrowser-approved-packages.json +++ b/common/config/rush/nonbrowser-approved-packages.json @@ -199,7 +199,7 @@ "allowedCategories": [ "libraries", "tests" ] }, { - "name": "@rushstack/heft-image-typings-generator-plugin", + "name": "@rushstack/heft-static-asset-typings-plugin", "allowedCategories": [ "libraries", "tests" ] }, { diff --git a/common/config/subspaces/default/pnpm-lock.yaml b/common/config/subspaces/default/pnpm-lock.yaml index 11328343a83..dc0d99fe30d 100644 --- a/common/config/subspaces/default/pnpm-lock.yaml +++ b/common/config/subspaces/default/pnpm-lock.yaml @@ -1995,6 +1995,9 @@ importers: '@rushstack/heft-lint-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-static-asset-typings-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-static-asset-typings-plugin '@rushstack/heft-typescript-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-typescript-plugin @@ -2095,9 +2098,6 @@ importers: '@rushstack/heft-dev-cert-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-dev-cert-plugin - '@rushstack/heft-image-typings-generator-plugin': - specifier: workspace:* - version: link:../../heft-plugins/heft-image-typings-generator-plugin '@rushstack/heft-jest-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-jest-plugin @@ -2107,6 +2107,9 @@ importers: '@rushstack/heft-rspack-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-rspack-plugin + '@rushstack/heft-static-asset-typings-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-static-asset-typings-plugin '@rushstack/heft-typescript-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-typescript-plugin @@ -2252,15 +2255,15 @@ importers: '@rushstack/heft': specifier: workspace:* version: link:../../apps/heft - '@rushstack/heft-image-typings-generator-plugin': - specifier: workspace:* - version: link:../../heft-plugins/heft-image-typings-generator-plugin '@rushstack/heft-jest-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-jest-plugin '@rushstack/heft-lint-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-static-asset-typings-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-static-asset-typings-plugin '@rushstack/heft-typescript-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-typescript-plugin @@ -2414,15 +2417,15 @@ importers: '@rushstack/heft-dev-cert-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-dev-cert-plugin - '@rushstack/heft-image-typings-generator-plugin': - specifier: workspace:* - version: link:../../heft-plugins/heft-image-typings-generator-plugin '@rushstack/heft-jest-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-jest-plugin '@rushstack/heft-lint-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-static-asset-typings-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-static-asset-typings-plugin '@rushstack/heft-typescript-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-typescript-plugin @@ -2477,15 +2480,15 @@ importers: '@rushstack/heft-dev-cert-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-dev-cert-plugin - '@rushstack/heft-image-typings-generator-plugin': - specifier: workspace:* - version: link:../../heft-plugins/heft-image-typings-generator-plugin '@rushstack/heft-jest-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-jest-plugin '@rushstack/heft-lint-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-static-asset-typings-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-static-asset-typings-plugin '@rushstack/heft-typescript-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-typescript-plugin @@ -3179,31 +3182,6 @@ importers: specifier: workspace:* version: link:../../rigs/local-node-rig - ../../../heft-plugins/heft-image-typings-generator-plugin: - dependencies: - '@rushstack/heft-config-file': - specifier: workspace:* - version: link:../../libraries/heft-config-file - '@rushstack/node-core-library': - specifier: workspace:* - version: link:../../libraries/node-core-library - '@rushstack/terminal': - specifier: workspace:* - version: link:../../libraries/terminal - '@rushstack/typings-generator': - specifier: workspace:* - version: link:../../libraries/typings-generator - devDependencies: - '@rushstack/heft': - specifier: workspace:* - version: link:../../apps/heft - eslint: - specifier: ~9.37.0 - version: 9.37.0 - local-node-rig: - specifier: workspace:* - version: link:../../rigs/local-node-rig - ../../../heft-plugins/heft-isolated-typescript-transpile-plugin: dependencies: '@rushstack/lookup-by-path': @@ -3504,6 +3482,31 @@ importers: specifier: workspace:* version: link:../../rigs/local-node-rig + ../../../heft-plugins/heft-static-asset-typings-plugin: + dependencies: + '@rushstack/heft-config-file': + specifier: workspace:* + version: link:../../libraries/heft-config-file + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + '@rushstack/typings-generator': + specifier: workspace:* + version: link:../../libraries/typings-generator + devDependencies: + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + eslint: + specifier: ~9.37.0 + version: 9.37.0 + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + ../../../heft-plugins/heft-storybook-plugin: dependencies: '@rushstack/node-core-library': @@ -4747,9 +4750,6 @@ importers: '@rushstack/heft-api-extractor-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-api-extractor-plugin - '@rushstack/heft-image-typings-generator-plugin': - specifier: workspace:* - version: link:../../heft-plugins/heft-image-typings-generator-plugin '@rushstack/heft-jest-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-jest-plugin @@ -4759,6 +4759,9 @@ importers: '@rushstack/heft-sass-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-sass-plugin + '@rushstack/heft-static-asset-typings-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-static-asset-typings-plugin '@rushstack/heft-typescript-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-typescript-plugin diff --git a/heft-plugins/heft-image-typings-generator-plugin/README.md b/heft-plugins/heft-image-typings-generator-plugin/README.md deleted file mode 100644 index 6f3cf4967a0..00000000000 --- a/heft-plugins/heft-image-typings-generator-plugin/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# @rushstack/heft-image-typings-generator-plugin - -This is a Heft plugin that generates TypeScript `.d.ts` typings for image files (`.png`, `.jpg`, -`.gif`, `.svg`, etc.). Each matched image file produces a typing that exports a default string -for the image URL, enabling type-safe image imports in TypeScript projects. - -## Setup - -1. Add the plugin as a `devDependency` of your project: - - ```bash - rush add -p @rushstack/heft-image-typings-generator-plugin --dev - ``` - -2. Load the plugin in your project's **config/heft.json** configuration: - - ```jsonc - { - "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", - "phasesByName": { - "build": { - "tasksByName": { - "image-typings": { - "taskPlugin": { - "pluginPackage": "@rushstack/heft-image-typings-generator-plugin", - "options": { - "fileExtensions": [".png", ".jpg", ".gif", ".svg"], - "generatedTsFolder": "temp/image-typings" - // "srcFolder": "src" // (optional, defaults to "src") - } - } - } - } - } - } - } - ``` - -3. Add the generated typings folder to your **tsconfig.json** `rootDirs` so that - TypeScript can resolve the declarations: - - ```jsonc - { - "compilerOptions": { - "rootDirs": ["src", "temp/image-typings"] - } - } - ``` - -## Plugin options - -| Option | Type | Default | Description | -| ------------------- | ---------- | ------- | ----------------------------------------------------------------- | -| `fileExtensions` | `string[]` | — | **(required)** File extensions to generate typings for, e.g. `[".png", ".jpg"]`. | -| `generatedTsFolder` | `string` | — | **(required)** Output folder for the generated `.d.ts` files. | -| `srcFolder` | `string` | `"src"` | Source folder to scan for image files. | - -## Links - -- [CHANGELOG.md]( - https://github.com/microsoft/rushstack/blob/main/heft-plugins/heft-image-typings-generator-plugin/CHANGELOG.md) - Find - out what's new in the latest version -- [@rushstack/heft](https://www.npmjs.com/package/@rushstack/heft) - Heft is a config-driven toolchain that invokes popular tools such as TypeScript, ESLint, Jest, Webpack, and API Extractor. - -Heft is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/heft-plugins/heft-image-typings-generator-plugin/.npmignore b/heft-plugins/heft-static-asset-typings-plugin/.npmignore similarity index 100% rename from heft-plugins/heft-image-typings-generator-plugin/.npmignore rename to heft-plugins/heft-static-asset-typings-plugin/.npmignore diff --git a/heft-plugins/heft-image-typings-generator-plugin/LICENSE b/heft-plugins/heft-static-asset-typings-plugin/LICENSE similarity index 95% rename from heft-plugins/heft-image-typings-generator-plugin/LICENSE rename to heft-plugins/heft-static-asset-typings-plugin/LICENSE index d6ceefdec90..9570e2a1a6f 100644 --- a/heft-plugins/heft-image-typings-generator-plugin/LICENSE +++ b/heft-plugins/heft-static-asset-typings-plugin/LICENSE @@ -1,4 +1,4 @@ -@rushstack/heft-image-typings-generator-plugin +@rushstack/heft-static-asset-typings-plugin Copyright (c) Microsoft Corporation. All rights reserved. diff --git a/heft-plugins/heft-static-asset-typings-plugin/README.md b/heft-plugins/heft-static-asset-typings-plugin/README.md new file mode 100644 index 00000000000..749bb997a94 --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/README.md @@ -0,0 +1,145 @@ +# @rushstack/heft-static-asset-typings-plugin + +This Heft plugin generates TypeScript `.d.ts` typings for static asset files, enabling type-safe +`import` statements for non-TypeScript resources. It provides two task plugins: + +- **`binary-assets-plugin`** — Generates `.d.ts` typings for binary files such as images (`.png`, + `.jpg`, `.svg`, etc.). Each matched file gets a typing that exports a default string value representing + the asset URL. + +- **`text-assets-plugin`** — Generates `.d.ts` typings _and_ JavaScript module output for text-based + files (`.html`, `.txt`, `.md`, etc.). The generated JS modules export the file contents as a + default string, making text assets importable as ES modules. + +Both plugins support incremental and watch-mode builds. + +## Setup + +1. Add the plugin as a `devDependency` of your project: + + ```bash + rush add -p @rushstack/heft-static-asset-typings-plugin --dev + ``` + +2. Load the appropriate plugin(s) in your project's **config/heft.json**: + + ### Binary assets (images, fonts, etc.) + + ```jsonc + { + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "phasesByName": { + "build": { + "tasksByName": { + "image-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "binary-assets-plugin", + "options": { + "configType": "inline", + "config": { + "fileExtensions": [".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp", ".avif"], + "generatedTsFolder": "temp/image-typings" + } + } + } + }, + "typescript": { + "taskDependencies": ["image-typings"] + // ... + } + } + } + } + } + ``` + + ### Text assets + + ```jsonc + { + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "phasesByName": { + "build": { + "tasksByName": { + "text-assets": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "text-assets-plugin", + "options": { + "cjsOutputFolders": ["lib-commonjs"], + "esmOutputFolders": ["lib-esm"] + // "configFileName": "text-assets.json" // (optional, name of riggable config file) + } + } + }, + "typescript": { + "taskDependencies": ["text-assets"] + // ... + } + } + } + } + } + ``` + +3. Add the generated typings folder to your **tsconfig.json** `rootDirs` so that + TypeScript can resolve the declarations: + + ```jsonc + { + "compilerOptions": { + "rootDirs": ["src", "temp/image-typings"] + } + } + ``` + +## Plugin options + +### `binary-assets-plugin` + +Supports two configuration modes: + +**Inline mode** (`configType: "inline"`): + +| Option | Type | Default | Description | +| ---------------------------- | ---------- | --------- | -------------------------------------------------------------------- | +| `config.fileExtensions` | `string[]` | — | **(required)** File extensions to generate typings for. | +| `config.generatedTsFolder` | `string` | `"temp/static-asset-typings"` | Output folder for the generated `.d.ts` files. | +| `config.secondaryGeneratedTsFolders` | `string[]` | `[]` | Additional output folders for generated `.d.ts` files. | +| `config.sourceFolderPath` | `string` | `"src"` | Source folder to scan for asset files. | + +**File mode** (`configType: "file"`): + +| Option | Type | Description | +| ---------------- | -------- | ---------------------------------------------------------------------- | +| `configFileName` | `string` | **(required)** Name of a riggable JSON config file in the `config/` folder. | + +### `text-assets-plugin` + +| Option | Type | Default | Description | +| ---------------- | ---------- | -------------------- | ---------------------------------------------------------------- | +| `cjsOutputFolders` | `string[]` | — | **(required)** Output folders for generated CommonJS `.js` modules. | +| `esmOutputFolders` | `string[]` | `[]` | Output folders for generated ESM `.js` modules. | +| `configFileName` | `string` | `"text-assets.json"` | Name of a riggable JSON config file in the `config/` folder. | + +## Riggable config file format + +Both plugins can load configuration from a riggable JSON config file (located in your project's +`config/` folder). The schema supports the following properties: + +| Property | Type | Default | Description | +| ---------------------------- | ---------- | --------- | -------------------------------------------------------------- | +| `fileExtensions` | `string[]` | — | **(required)** File extensions to process. | +| `generatedTsFolder` | `string` | `"temp/static-asset-typings"` | Output folder for generated `.d.ts` typings. | +| `secondaryGeneratedTsFolders`| `string[]` | `[]` | Additional output folders for generated typings. | +| `sourceFolderPath` | `string` | `"src"` | Source folder to scan for matching files. | + +## Links + +- [CHANGELOG.md]( + https://github.com/microsoft/rushstack/blob/main/heft-plugins/heft-static-asset-typings-plugin/CHANGELOG.md) - Find + out what's new in the latest version +- [@rushstack/heft](https://www.npmjs.com/package/@rushstack/heft) - Heft is a config-driven toolchain that invokes popular tools such as TypeScript, ESLint, Jest, Webpack, and API Extractor. + +Heft is part of the [Rush Stack](https://rushstack.io/) family of projects. diff --git a/heft-plugins/heft-image-typings-generator-plugin/config/heft.json b/heft-plugins/heft-static-asset-typings-plugin/config/heft.json similarity index 100% rename from heft-plugins/heft-image-typings-generator-plugin/config/heft.json rename to heft-plugins/heft-static-asset-typings-plugin/config/heft.json diff --git a/heft-plugins/heft-image-typings-generator-plugin/config/jest.config.json b/heft-plugins/heft-static-asset-typings-plugin/config/jest.config.json similarity index 100% rename from heft-plugins/heft-image-typings-generator-plugin/config/jest.config.json rename to heft-plugins/heft-static-asset-typings-plugin/config/jest.config.json diff --git a/heft-plugins/heft-image-typings-generator-plugin/config/rig.json b/heft-plugins/heft-static-asset-typings-plugin/config/rig.json similarity index 100% rename from heft-plugins/heft-image-typings-generator-plugin/config/rig.json rename to heft-plugins/heft-static-asset-typings-plugin/config/rig.json diff --git a/heft-plugins/heft-image-typings-generator-plugin/eslint.config.js b/heft-plugins/heft-static-asset-typings-plugin/eslint.config.js similarity index 100% rename from heft-plugins/heft-image-typings-generator-plugin/eslint.config.js rename to heft-plugins/heft-static-asset-typings-plugin/eslint.config.js diff --git a/heft-plugins/heft-image-typings-generator-plugin/heft-plugin.json b/heft-plugins/heft-static-asset-typings-plugin/heft-plugin.json similarity index 100% rename from heft-plugins/heft-image-typings-generator-plugin/heft-plugin.json rename to heft-plugins/heft-static-asset-typings-plugin/heft-plugin.json diff --git a/heft-plugins/heft-image-typings-generator-plugin/package.json b/heft-plugins/heft-static-asset-typings-plugin/package.json similarity index 82% rename from heft-plugins/heft-image-typings-generator-plugin/package.json rename to heft-plugins/heft-static-asset-typings-plugin/package.json index d52125f7430..e5428721714 100644 --- a/heft-plugins/heft-image-typings-generator-plugin/package.json +++ b/heft-plugins/heft-static-asset-typings-plugin/package.json @@ -1,7 +1,7 @@ { - "name": "@rushstack/heft-image-typings-generator-plugin", + "name": "@rushstack/heft-static-asset-typings-plugin", "version": "0.0.0", - "description": "Generate typings for images.", + "description": "A Heft plugin that generates TypeScript typings for static asset files such as images and text files.", "scripts": { "build": "heft build --clean", "start": "heft test --clean --watch", @@ -10,7 +10,7 @@ "repository": { "type": "git", "url": "https://github.com/microsoft/rushstack.git", - "directory": "heft-plugins/heft-image-typings-generator-plugin" + "directory": "heft-plugins/heft-static-asset-typings-plugin" }, "homepage": "https://rushstack.io/pages/heft/overview/", "exports": { diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/BinaryAssetsPlugin.ts b/heft-plugins/heft-static-asset-typings-plugin/src/BinaryAssetsPlugin.ts similarity index 100% rename from heft-plugins/heft-image-typings-generator-plugin/src/BinaryAssetsPlugin.ts rename to heft-plugins/heft-static-asset-typings-plugin/src/BinaryAssetsPlugin.ts diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/StaticAssetTypingsGenerator.ts b/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts similarity index 100% rename from heft-plugins/heft-image-typings-generator-plugin/src/StaticAssetTypingsGenerator.ts rename to heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/TextAssetsPlugin.ts b/heft-plugins/heft-static-asset-typings-plugin/src/TextAssetsPlugin.ts similarity index 100% rename from heft-plugins/heft-image-typings-generator-plugin/src/TextAssetsPlugin.ts rename to heft-plugins/heft-static-asset-typings-plugin/src/TextAssetsPlugin.ts diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/getConfigFromConfigFileAsync.ts b/heft-plugins/heft-static-asset-typings-plugin/src/getConfigFromConfigFileAsync.ts similarity index 100% rename from heft-plugins/heft-image-typings-generator-plugin/src/getConfigFromConfigFileAsync.ts rename to heft-plugins/heft-static-asset-typings-plugin/src/getConfigFromConfigFileAsync.ts diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/schemas/binary-assets-options.schema.json b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/binary-assets-options.schema.json similarity index 100% rename from heft-plugins/heft-image-typings-generator-plugin/src/schemas/binary-assets-options.schema.json rename to heft-plugins/heft-static-asset-typings-plugin/src/schemas/binary-assets-options.schema.json diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/schemas/static-asset-typings.schema.json b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/static-asset-typings.schema.json similarity index 100% rename from heft-plugins/heft-image-typings-generator-plugin/src/schemas/static-asset-typings.schema.json rename to heft-plugins/heft-static-asset-typings-plugin/src/schemas/static-asset-typings.schema.json diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/schemas/text-assets-options.schema.json b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/text-assets-options.schema.json similarity index 100% rename from heft-plugins/heft-image-typings-generator-plugin/src/schemas/text-assets-options.schema.json rename to heft-plugins/heft-static-asset-typings-plugin/src/schemas/text-assets-options.schema.json diff --git a/heft-plugins/heft-image-typings-generator-plugin/src/types.ts b/heft-plugins/heft-static-asset-typings-plugin/src/types.ts similarity index 100% rename from heft-plugins/heft-image-typings-generator-plugin/src/types.ts rename to heft-plugins/heft-static-asset-typings-plugin/src/types.ts diff --git a/heft-plugins/heft-image-typings-generator-plugin/tsconfig.json b/heft-plugins/heft-static-asset-typings-plugin/tsconfig.json similarity index 100% rename from heft-plugins/heft-image-typings-generator-plugin/tsconfig.json rename to heft-plugins/heft-static-asset-typings-plugin/tsconfig.json diff --git a/rigs/heft-web-rig/package.json b/rigs/heft-web-rig/package.json index 59f8c01839e..12c10225e3e 100644 --- a/rigs/heft-web-rig/package.json +++ b/rigs/heft-web-rig/package.json @@ -19,7 +19,7 @@ "@microsoft/api-extractor": "workspace:*", "@rushstack/eslint-config": "workspace:*", "@rushstack/heft-api-extractor-plugin": "workspace:*", - "@rushstack/heft-image-typings-generator-plugin": "workspace:*", + "@rushstack/heft-static-asset-typings-plugin": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-sass-plugin": "workspace:*", diff --git a/rigs/heft-web-rig/profiles/app/config/heft.json b/rigs/heft-web-rig/profiles/app/config/heft.json index 9dd36c11dbf..45a469fbaf0 100644 --- a/rigs/heft-web-rig/profiles/app/config/heft.json +++ b/rigs/heft-web-rig/profiles/app/config/heft.json @@ -50,7 +50,7 @@ "image-typings": { "taskDependencies": ["set-browserslist-ignore-old-data-env-var"], "taskPlugin": { - "pluginPackage": "@rushstack/heft-image-typings-generator-plugin", + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", "options": { "fileExtensions": [".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp", ".avif"], "generatedTsFolder": "temp/image-typings" diff --git a/rigs/heft-web-rig/profiles/library/config/heft.json b/rigs/heft-web-rig/profiles/library/config/heft.json index 9dd36c11dbf..45a469fbaf0 100644 --- a/rigs/heft-web-rig/profiles/library/config/heft.json +++ b/rigs/heft-web-rig/profiles/library/config/heft.json @@ -50,7 +50,7 @@ "image-typings": { "taskDependencies": ["set-browserslist-ignore-old-data-env-var"], "taskPlugin": { - "pluginPackage": "@rushstack/heft-image-typings-generator-plugin", + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", "options": { "fileExtensions": [".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp", ".avif"], "generatedTsFolder": "temp/image-typings" diff --git a/rush.json b/rush.json index 90b19beab9b..cced18d1f10 100644 --- a/rush.json +++ b/rush.json @@ -1096,12 +1096,6 @@ "reviewCategory": "libraries", "shouldPublish": true }, - { - "packageName": "@rushstack/heft-image-typings-generator-plugin", - "projectFolder": "heft-plugins/heft-image-typings-generator-plugin", - "reviewCategory": "libraries", - "shouldPublish": true - }, { "packageName": "@rushstack/heft-isolated-typescript-transpile-plugin", "projectFolder": "heft-plugins/heft-isolated-typescript-transpile-plugin", @@ -1156,6 +1150,12 @@ "reviewCategory": "libraries", "shouldPublish": true }, + { + "packageName": "@rushstack/heft-static-asset-typings-plugin", + "projectFolder": "heft-plugins/heft-static-asset-typings-plugin", + "reviewCategory": "libraries", + "shouldPublish": true + }, { "packageName": "@rushstack/heft-storybook-plugin", "projectFolder": "heft-plugins/heft-storybook-plugin", From c724e13486e3de35c9509e0f4414e07f8cf8ba34 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Mon, 23 Feb 2026 14:35:52 -0800 Subject: [PATCH 06/17] fixup! Rewrite the plugin to handle general static assets. --- .../heft-web-rig-app-tutorial/src/ExampleApp.tsx | 1 + build-tests/heft-rspack-everything-test/config/heft.json | 8 ++++++-- .../heft-webpack4-everything-test/config/heft.json | 8 ++++++-- .../heft-webpack5-everything-test/config/heft.json | 8 ++++++-- rigs/heft-web-rig/profiles/app/config/heft.json | 8 ++++++-- rigs/heft-web-rig/profiles/library/config/heft.json | 8 ++++++-- 6 files changed, 31 insertions(+), 10 deletions(-) diff --git a/build-tests-samples/heft-web-rig-app-tutorial/src/ExampleApp.tsx b/build-tests-samples/heft-web-rig-app-tutorial/src/ExampleApp.tsx index 7cbb226945d..3ef0dfdafab 100644 --- a/build-tests-samples/heft-web-rig-app-tutorial/src/ExampleApp.tsx +++ b/build-tests-samples/heft-web-rig-app-tutorial/src/ExampleApp.tsx @@ -3,6 +3,7 @@ import * as React from 'react'; import { ToggleSwitch, type IToggleEventArgs } from 'heft-web-rig-library-tutorial'; + import exampleImage from './example-image.png'; /** diff --git a/build-tests/heft-rspack-everything-test/config/heft.json b/build-tests/heft-rspack-everything-test/config/heft.json index 290c43699ee..76d98cebe77 100644 --- a/build-tests/heft-rspack-everything-test/config/heft.json +++ b/build-tests/heft-rspack-everything-test/config/heft.json @@ -13,9 +13,13 @@ "image-typings": { "taskPlugin": { "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "binary-assets-plugin", "options": { - "fileExtensions": [".png"], - "generatedTsFolder": "temp/image-typings" + "configType": "inline", + "config": { + "fileExtensions": [".png"], + "generatedTsFolder": "temp/image-typings" + } } } }, diff --git a/build-tests/heft-webpack4-everything-test/config/heft.json b/build-tests/heft-webpack4-everything-test/config/heft.json index 7d03ef3c7e9..3cd0d0b48c9 100644 --- a/build-tests/heft-webpack4-everything-test/config/heft.json +++ b/build-tests/heft-webpack4-everything-test/config/heft.json @@ -13,9 +13,13 @@ "image-typings": { "taskPlugin": { "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "binary-assets-plugin", "options": { - "fileExtensions": [".png"], - "generatedTsFolder": "temp/image-typings" + "configType": "inline", + "config": { + "fileExtensions": [".png"], + "generatedTsFolder": "temp/image-typings" + } } } }, diff --git a/build-tests/heft-webpack5-everything-test/config/heft.json b/build-tests/heft-webpack5-everything-test/config/heft.json index 7dd78bf182a..944fe7efd56 100644 --- a/build-tests/heft-webpack5-everything-test/config/heft.json +++ b/build-tests/heft-webpack5-everything-test/config/heft.json @@ -13,9 +13,13 @@ "image-typings": { "taskPlugin": { "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "binary-assets-plugin", "options": { - "fileExtensions": [".png"], - "generatedTsFolder": "temp/image-typings" + "configType": "inline", + "config": { + "fileExtensions": [".png"], + "generatedTsFolder": "temp/image-typings" + } } } }, diff --git a/rigs/heft-web-rig/profiles/app/config/heft.json b/rigs/heft-web-rig/profiles/app/config/heft.json index 45a469fbaf0..6e455df82d0 100644 --- a/rigs/heft-web-rig/profiles/app/config/heft.json +++ b/rigs/heft-web-rig/profiles/app/config/heft.json @@ -51,9 +51,13 @@ "taskDependencies": ["set-browserslist-ignore-old-data-env-var"], "taskPlugin": { "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "binary-assets-plugin", "options": { - "fileExtensions": [".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp", ".avif"], - "generatedTsFolder": "temp/image-typings" + "configType": "inline", + "config": { + "fileExtensions": [".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp", ".avif"], + "generatedTsFolder": "temp/image-typings" + } } } }, diff --git a/rigs/heft-web-rig/profiles/library/config/heft.json b/rigs/heft-web-rig/profiles/library/config/heft.json index 45a469fbaf0..6e455df82d0 100644 --- a/rigs/heft-web-rig/profiles/library/config/heft.json +++ b/rigs/heft-web-rig/profiles/library/config/heft.json @@ -51,9 +51,13 @@ "taskDependencies": ["set-browserslist-ignore-old-data-env-var"], "taskPlugin": { "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "binary-assets-plugin", "options": { - "fileExtensions": [".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp", ".avif"], - "generatedTsFolder": "temp/image-typings" + "configType": "inline", + "config": { + "fileExtensions": [".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp", ".avif"], + "generatedTsFolder": "temp/image-typings" + } } } }, From cb1f1fec734f3729ab3921d498209a81142f7f90 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Mon, 23 Feb 2026 14:37:02 -0800 Subject: [PATCH 07/17] Remove usage of the plugin in the heft-typescript-composite-test project. --- .../heft-typescript-composite-test/config/heft.json | 10 ---------- .../heft-typescript-composite-test/package.json | 1 - .../src/chunks/ChunkClass.ts | 5 ++--- .../heft-typescript-composite-test/tsconfig-base.json | 2 +- common/config/subspaces/default/pnpm-lock.yaml | 3 --- 5 files changed, 3 insertions(+), 18 deletions(-) diff --git a/build-tests/heft-typescript-composite-test/config/heft.json b/build-tests/heft-typescript-composite-test/config/heft.json index 95d95bc6bab..c0cc9f94228 100644 --- a/build-tests/heft-typescript-composite-test/config/heft.json +++ b/build-tests/heft-typescript-composite-test/config/heft.json @@ -10,17 +10,7 @@ "cleanFiles": [{ "includeGlobs": ["lib", "lib-commonjs"] }], "tasksByName": { - "image-typings": { - "taskPlugin": { - "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", - "options": { - "fileExtensions": [".png"], - "generatedTsFolder": "temp/image-typings" - } - } - }, "typescript": { - "taskDependencies": ["image-typings"], "taskPlugin": { "pluginPackage": "@rushstack/heft-typescript-plugin" } diff --git a/build-tests/heft-typescript-composite-test/package.json b/build-tests/heft-typescript-composite-test/package.json index 584f983703b..48dba7975a5 100644 --- a/build-tests/heft-typescript-composite-test/package.json +++ b/build-tests/heft-typescript-composite-test/package.json @@ -11,7 +11,6 @@ }, "devDependencies": { "@rushstack/heft": "workspace:*", - "@rushstack/heft-static-asset-typings-plugin": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", diff --git a/build-tests/heft-typescript-composite-test/src/chunks/ChunkClass.ts b/build-tests/heft-typescript-composite-test/src/chunks/ChunkClass.ts index 50e541f593d..ddbf7d148c7 100644 --- a/build-tests/heft-typescript-composite-test/src/chunks/ChunkClass.ts +++ b/build-tests/heft-typescript-composite-test/src/chunks/ChunkClass.ts @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import image from './image.png'; - export class ChunkClass { public doStuff(): void { // eslint-disable-next-line no-console @@ -10,6 +8,7 @@ export class ChunkClass { } public getImageUrl(): string { - return image; + // eslint-disable-next-line @typescript-eslint/no-require-imports + return require('./image.png'); } } diff --git a/build-tests/heft-typescript-composite-test/tsconfig-base.json b/build-tests/heft-typescript-composite-test/tsconfig-base.json index 5b94a0d7cd7..b5bf6469742 100644 --- a/build-tests/heft-typescript-composite-test/tsconfig-base.json +++ b/build-tests/heft-typescript-composite-test/tsconfig-base.json @@ -3,7 +3,7 @@ "compilerOptions": { "outDir": "lib", - "rootDirs": ["src", "temp/image-typings"], + "rootDir": "src", "forceConsistentCasingInFileNames": true, "jsx": "react", diff --git a/common/config/subspaces/default/pnpm-lock.yaml b/common/config/subspaces/default/pnpm-lock.yaml index dc0d99fe30d..c55d9cfd060 100644 --- a/common/config/subspaces/default/pnpm-lock.yaml +++ b/common/config/subspaces/default/pnpm-lock.yaml @@ -2261,9 +2261,6 @@ importers: '@rushstack/heft-lint-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-lint-plugin - '@rushstack/heft-static-asset-typings-plugin': - specifier: workspace:* - version: link:../../heft-plugins/heft-static-asset-typings-plugin '@rushstack/heft-typescript-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-typescript-plugin From 5f62fa1e68417a5be828c3e101f576fafeb58e21 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Mon, 23 Feb 2026 14:44:10 -0800 Subject: [PATCH 08/17] fixup! Use the new plugin in build-tests projects. --- build-tests/heft-webpack4-everything-test/tsconfig.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build-tests/heft-webpack4-everything-test/tsconfig.json b/build-tests/heft-webpack4-everything-test/tsconfig.json index fca9a5d7b34..ebef84f93fc 100644 --- a/build-tests/heft-webpack4-everything-test/tsconfig.json +++ b/build-tests/heft-webpack4-everything-test/tsconfig.json @@ -6,6 +6,8 @@ "rootDirs": ["src", "temp/image-typings"], "forceConsistentCasingInFileNames": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, "jsx": "react", "declaration": true, "sourceMap": true, From 5edc1d1c2bc5b666eada87a09acb6d7ac7ac46c2 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Mon, 23 Feb 2026 14:48:22 -0800 Subject: [PATCH 09/17] fixup! Rewrite the plugin to handle general static assets. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5b31b04f277..e49d4c1c729 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,6 @@ These GitHub repositories provide supplementary resources for Rush Stack: | [/eslint/eslint-plugin-security](./eslint/eslint-plugin-security/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Feslint-plugin-security.svg)](https://badge.fury.io/js/%40rushstack%2Feslint-plugin-security) | [changelog](./eslint/eslint-plugin-security/CHANGELOG.md) | [@rushstack/eslint-plugin-security](https://www.npmjs.com/package/@rushstack/eslint-plugin-security) | | [/heft-plugins/heft-api-extractor-plugin](./heft-plugins/heft-api-extractor-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-api-extractor-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-api-extractor-plugin) | [changelog](./heft-plugins/heft-api-extractor-plugin/CHANGELOG.md) | [@rushstack/heft-api-extractor-plugin](https://www.npmjs.com/package/@rushstack/heft-api-extractor-plugin) | | [/heft-plugins/heft-dev-cert-plugin](./heft-plugins/heft-dev-cert-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-dev-cert-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-dev-cert-plugin) | [changelog](./heft-plugins/heft-dev-cert-plugin/CHANGELOG.md) | [@rushstack/heft-dev-cert-plugin](https://www.npmjs.com/package/@rushstack/heft-dev-cert-plugin) | -| [/heft-plugins/heft-image-typings-generator-plugin](./heft-plugins/heft-image-typings-generator-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-image-typings-generator-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-image-typings-generator-plugin) | [changelog](./heft-plugins/heft-image-typings-generator-plugin/CHANGELOG.md) | [@rushstack/heft-image-typings-generator-plugin](https://www.npmjs.com/package/@rushstack/heft-image-typings-generator-plugin) | | [/heft-plugins/heft-isolated-typescript-transpile-plugin](./heft-plugins/heft-isolated-typescript-transpile-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-isolated-typescript-transpile-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-isolated-typescript-transpile-plugin) | [changelog](./heft-plugins/heft-isolated-typescript-transpile-plugin/CHANGELOG.md) | [@rushstack/heft-isolated-typescript-transpile-plugin](https://www.npmjs.com/package/@rushstack/heft-isolated-typescript-transpile-plugin) | | [/heft-plugins/heft-jest-plugin](./heft-plugins/heft-jest-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-jest-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-jest-plugin) | [changelog](./heft-plugins/heft-jest-plugin/CHANGELOG.md) | [@rushstack/heft-jest-plugin](https://www.npmjs.com/package/@rushstack/heft-jest-plugin) | | [/heft-plugins/heft-json-schema-typings-plugin](./heft-plugins/heft-json-schema-typings-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-json-schema-typings-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-json-schema-typings-plugin) | [changelog](./heft-plugins/heft-json-schema-typings-plugin/CHANGELOG.md) | [@rushstack/heft-json-schema-typings-plugin](https://www.npmjs.com/package/@rushstack/heft-json-schema-typings-plugin) | @@ -77,6 +76,7 @@ These GitHub repositories provide supplementary resources for Rush Stack: | [/heft-plugins/heft-sass-load-themed-styles-plugin](./heft-plugins/heft-sass-load-themed-styles-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-sass-load-themed-styles-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-sass-load-themed-styles-plugin) | [changelog](./heft-plugins/heft-sass-load-themed-styles-plugin/CHANGELOG.md) | [@rushstack/heft-sass-load-themed-styles-plugin](https://www.npmjs.com/package/@rushstack/heft-sass-load-themed-styles-plugin) | | [/heft-plugins/heft-sass-plugin](./heft-plugins/heft-sass-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-sass-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-sass-plugin) | [changelog](./heft-plugins/heft-sass-plugin/CHANGELOG.md) | [@rushstack/heft-sass-plugin](https://www.npmjs.com/package/@rushstack/heft-sass-plugin) | | [/heft-plugins/heft-serverless-stack-plugin](./heft-plugins/heft-serverless-stack-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-serverless-stack-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-serverless-stack-plugin) | [changelog](./heft-plugins/heft-serverless-stack-plugin/CHANGELOG.md) | [@rushstack/heft-serverless-stack-plugin](https://www.npmjs.com/package/@rushstack/heft-serverless-stack-plugin) | +| [/heft-plugins/heft-static-asset-typings-plugin](./heft-plugins/heft-static-asset-typings-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-static-asset-typings-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-static-asset-typings-plugin) | [changelog](./heft-plugins/heft-static-asset-typings-plugin/CHANGELOG.md) | [@rushstack/heft-static-asset-typings-plugin](https://www.npmjs.com/package/@rushstack/heft-static-asset-typings-plugin) | | [/heft-plugins/heft-storybook-plugin](./heft-plugins/heft-storybook-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-storybook-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-storybook-plugin) | [changelog](./heft-plugins/heft-storybook-plugin/CHANGELOG.md) | [@rushstack/heft-storybook-plugin](https://www.npmjs.com/package/@rushstack/heft-storybook-plugin) | | [/heft-plugins/heft-typescript-plugin](./heft-plugins/heft-typescript-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-typescript-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-typescript-plugin) | [changelog](./heft-plugins/heft-typescript-plugin/CHANGELOG.md) | [@rushstack/heft-typescript-plugin](https://www.npmjs.com/package/@rushstack/heft-typescript-plugin) | | [/heft-plugins/heft-vscode-extension-plugin](./heft-plugins/heft-vscode-extension-plugin/) | [![npm version](https://badge.fury.io/js/%40rushstack%2Fheft-vscode-extension-plugin.svg)](https://badge.fury.io/js/%40rushstack%2Fheft-vscode-extension-plugin) | [changelog](./heft-plugins/heft-vscode-extension-plugin/CHANGELOG.md) | [@rushstack/heft-vscode-extension-plugin](https://www.npmjs.com/package/@rushstack/heft-vscode-extension-plugin) | From 762d71ee432c3c8a48861a5d1ec63be2b3761bf9 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Mon, 23 Feb 2026 14:52:55 -0800 Subject: [PATCH 10/17] Add unit test. --- .../package.json | 3 +- .../src/StaticAssetTypingsGenerator.ts | 6 +- .../test/StaticAssetTypingsGenerator.test.ts | 72 +++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 heft-plugins/heft-static-asset-typings-plugin/src/test/StaticAssetTypingsGenerator.test.ts diff --git a/heft-plugins/heft-static-asset-typings-plugin/package.json b/heft-plugins/heft-static-asset-typings-plugin/package.json index e5428721714..ed0af701c28 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/package.json +++ b/heft-plugins/heft-static-asset-typings-plugin/package.json @@ -5,7 +5,8 @@ "scripts": { "build": "heft build --clean", "start": "heft test --clean --watch", - "_phase:build": "heft run --only build -- --clean" + "_phase:build": "heft run --only build -- --clean", + "_phase:test": "heft run --only test -- --clean" }, "repository": { "type": "git", diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts b/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts index ac010770b5e..256d65fc769 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts +++ b/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts @@ -245,7 +245,11 @@ async function runTypingsGeneratorIncrementalAsync( terminal.writeLine('Finished processing static assets.'); } -function hasChanges(current: ReadonlyMap, old: ReadonlyMap): boolean { +/** + * @internal + * Returns true if the current map has different entries than the old map. + */ +export function hasChanges(current: ReadonlyMap, old: ReadonlyMap): boolean { if (current.size !== old.size) { return true; } diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/test/StaticAssetTypingsGenerator.test.ts b/heft-plugins/heft-static-asset-typings-plugin/src/test/StaticAssetTypingsGenerator.test.ts new file mode 100644 index 00000000000..bc550c55ce6 --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/src/test/StaticAssetTypingsGenerator.test.ts @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { hasChanges } from '../StaticAssetTypingsGenerator'; + +describe('hasChanges', () => { + it('returns false for two empty maps', () => { + const current: Map = new Map(); + const old: Map = new Map(); + expect(hasChanges(current, old)).toBe(false); + }); + + it('returns false for identical maps', () => { + const current: Map = new Map([ + ['a.png', 'v1'], + ['b.png', 'v2'] + ]); + const old: Map = new Map([ + ['a.png', 'v1'], + ['b.png', 'v2'] + ]); + expect(hasChanges(current, old)).toBe(false); + }); + + it('returns true when current has more entries', () => { + const current: Map = new Map([ + ['a.png', 'v1'], + ['b.png', 'v2'] + ]); + const old: Map = new Map([['a.png', 'v1']]); + expect(hasChanges(current, old)).toBe(true); + }); + + it('returns true when old has more entries', () => { + const current: Map = new Map([['a.png', 'v1']]); + const old: Map = new Map([ + ['a.png', 'v1'], + ['b.png', 'v2'] + ]); + expect(hasChanges(current, old)).toBe(true); + }); + + it('returns true when a value differs', () => { + const current: Map = new Map([ + ['a.png', 'v1'], + ['b.png', 'v3'] + ]); + const old: Map = new Map([ + ['a.png', 'v1'], + ['b.png', 'v2'] + ]); + expect(hasChanges(current, old)).toBe(true); + }); + + it('returns true when a key differs', () => { + const current: Map = new Map([['a.png', 'v1']]); + const old: Map = new Map([['b.png', 'v1']]); + expect(hasChanges(current, old)).toBe(true); + }); + + it('returns true when current is empty and old has entries', () => { + const current: Map = new Map(); + const old: Map = new Map([['a.png', 'v1']]); + expect(hasChanges(current, old)).toBe(true); + }); + + it('returns true when current has entries and old is empty', () => { + const current: Map = new Map([['a.png', 'v1']]); + const old: Map = new Map(); + expect(hasChanges(current, old)).toBe(true); + }); +}); From 99c90c610f0f28f3bac60149bee1c9685044c170 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Mon, 23 Feb 2026 15:52:06 -0800 Subject: [PATCH 11/17] Rationalzie the plugin options. --- .../src/BinaryAssetsPlugin.ts | 74 ++----- .../src/StaticAssetTypingsGenerator.ts | 91 +++++--- .../src/TextAssetsPlugin.ts | 199 +++++++++--------- .../src/getConfigFromConfigFileAsync.ts | 43 ++-- .../schemas/binary-assets-options.schema.json | 19 +- ...> binary-static-asset-typings.schema.json} | 11 +- .../schemas/test-asset-typings.schema.json | 43 ++++ .../schemas/text-assets-options.schema.json | 85 ++++++-- .../src/types.ts | 39 +++- 9 files changed, 362 insertions(+), 242 deletions(-) rename heft-plugins/heft-static-asset-typings-plugin/src/schemas/{static-asset-typings.schema.json => binary-static-asset-typings.schema.json} (75%) create mode 100644 heft-plugins/heft-static-asset-typings-plugin/src/schemas/test-asset-typings.schema.json diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/BinaryAssetsPlugin.ts b/heft-plugins/heft-static-asset-typings-plugin/src/BinaryAssetsPlugin.ts index 9887dd21f49..219aae3793d 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/BinaryAssetsPlugin.ts +++ b/heft-plugins/heft-static-asset-typings-plugin/src/BinaryAssetsPlugin.ts @@ -2,76 +2,42 @@ // See LICENSE in the project root for license information. import type { HeftConfiguration, IHeftTaskSession, IHeftTaskPlugin } from '@rushstack/heft'; -import { Path } from '@rushstack/node-core-library'; -import type { ITerminal } from '@rushstack/terminal'; import { createTypingsGeneratorAsync, + tryGetConfigFromPluginOptionsAsync, type IRunGeneratorOptions, type IStaticAssetGeneratorOptions, type IStaticAssetTypingsGenerator } from './StaticAssetTypingsGenerator'; -import type { IStaticAssetTypingsConfigurationJson } from './types'; +import type { IAssetPluginOptions, IBinaryStaticAssetTypingsConfigurationJson } from './types'; const PLUGIN_NAME: 'static-asset-typings-plugin' = 'static-asset-typings-plugin'; -export interface IBinaryAssetsInlineConfigPluginOptions { - configType: 'inline'; - /** - * The inline configuration object. - */ - config: IStaticAssetTypingsConfigurationJson; -} - -export interface IBinaryAssetsFileConfigPluginOptions { - configType: 'file'; - /** - * The name of the riggable config file in the config/ folder. - */ - configFileName: string; -} - -export type IBinaryAssetPluginOptions = - | IBinaryAssetsInlineConfigPluginOptions - | IBinaryAssetsFileConfigPluginOptions; - -export default class BinaryAssetsPlugin implements IHeftTaskPlugin { +export default class BinaryAssetsPlugin + implements IHeftTaskPlugin> +{ /** * Generate typings for text files before TypeScript compilation. */ public apply( taskSession: IHeftTaskSession, heftConfiguration: HeftConfiguration, - options: IBinaryAssetPluginOptions + options: IAssetPluginOptions ): void { - const slashNormalizedBuildFolderPath: string = Path.convertToSlashes(heftConfiguration.buildFolderPath); - - async function getVersionAndEmitOutputFilesAsync( - relativePath: string, - filePath: string, - oldVersion: string | undefined - ): Promise { - return 'versionless'; - } - - async function tryGetConfigAsync( - terminal: ITerminal, - buildFolder: string, - rigConfig: HeftConfiguration['rigConfig'] - ): Promise { - if (options?.configType === 'inline') { - return options.config; - } else { - const { getConfigFromConfigFileAsync } = await import('./getConfigFromConfigFileAsync'); - const { configFileName } = options as IBinaryAssetsFileConfigPluginOptions; - return getConfigFromConfigFileAsync(configFileName, terminal, buildFolder, rigConfig); - } - } - + const { slashNormalizedBuildFolderPath, rigConfig } = heftConfiguration; const staticAssetGeneratorOptions: IStaticAssetGeneratorOptions = { - tryGetConfigAsync, + tryGetConfigAsync: async (terminal) => { + return await tryGetConfigFromPluginOptionsAsync( + terminal, + slashNormalizedBuildFolderPath, + rigConfig, + options, + 'binary' + ); + }, slashNormalizedBuildFolderPath, - getVersionAndEmitOutputFilesAsync + getVersionAndEmitOutputFilesAsync: async () => 'versionless' }; let generator: IStaticAssetTypingsGenerator | undefined | false; @@ -79,11 +45,7 @@ export default class BinaryAssetsPlugin implements IHeftTaskPlugin { if (generator === undefined) { // eslint-disable-next-line require-atomic-updates - generator = await createTypingsGeneratorAsync( - taskSession, - heftConfiguration, - staticAssetGeneratorOptions - ); + generator = await createTypingsGeneratorAsync(taskSession, staticAssetGeneratorOptions); } if (generator === false) { diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts b/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts index 256d65fc769..1d480e53ff1 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts +++ b/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts @@ -10,8 +10,14 @@ import type { } from '@rushstack/heft'; import { TypingsGenerator } from '@rushstack/typings-generator'; import { FileSystem, Sort } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; -import type { IStaticAssetTypingsConfigurationJson, StaticAssetConfigurationFileLoader } from './types'; +import type { + IAssetPluginOptions, + IBinaryStaticAssetTypingsConfigurationJson, + ITextStaticAssetTypingsConfigurationJson, + StaticAssetConfigurationFileLoader +} from './types'; const DECLARATION: string = `/** * @public @@ -30,18 +36,19 @@ export interface IStaticAssetGeneratorOptions { * A getter for the loader for the riggable config file in the project. */ tryGetConfigAsync: StaticAssetConfigurationFileLoader; + /** * The path to the build folder, normalized to use forward slashes as the directory separator. */ slashNormalizedBuildFolderPath: string; + /** - * * @param relativePath - The relative path of the file to get additional output files for. * @returns An array of output file names. */ getAdditionalOutputFiles?: (relativePath: string) => string[]; + /** - * * @param relativePath - The relative path of the file being processed. * @param filePath - The absolute path of the file being processed. * @param oldVersion - The old version of the file, if any. @@ -67,63 +74,87 @@ export interface IStaticAssetTypingsGenerator { runIncrementalAsync: (runOptions: IRunGeneratorOptions) => Promise; } -interface IStaticAssetTypingsConfiguration extends IStaticAssetTypingsConfigurationJson { - srcFolder: string; - generatedTsFolder: string; -} - interface IStaticAssetTypingsBuildInfoFile { fileVersions: [string, string][]; pluginVersion: number; } +export async function tryGetConfigFromPluginOptionsAsync( + terminal: ITerminal, + buildFolder: string, + rigConfig: HeftConfiguration['rigConfig'], + options: IAssetPluginOptions, + type: 'binary' +): Promise; +export async function tryGetConfigFromPluginOptionsAsync( + terminal: ITerminal, + buildFolder: string, + rigConfig: HeftConfiguration['rigConfig'], + options: IAssetPluginOptions, + type: 'text' +): Promise; +export async function tryGetConfigFromPluginOptionsAsync( + terminal: ITerminal, + buildFolder: string, + rigConfig: HeftConfiguration['rigConfig'], + options: IAssetPluginOptions< + IBinaryStaticAssetTypingsConfigurationJson | ITextStaticAssetTypingsConfigurationJson + >, + type: import('./getConfigFromConfigFileAsync').FileLoaderType +): Promise< + IBinaryStaticAssetTypingsConfigurationJson | ITextStaticAssetTypingsConfigurationJson | undefined +> { + if (options?.configType === 'inline') { + return options.config; + } else { + const { getConfigFromConfigFileAsync } = await import('./getConfigFromConfigFileAsync'); + const { configFileName } = options; + return getConfigFromConfigFileAsync(configFileName, type, terminal, buildFolder, rigConfig); + } +} + /** * Constructs a typings generator for processing static assets * * @param taskSession - The Heft task session - * @param heftConfiguration - The Heft configuration + * @param rigConfig - The Heft configuration * @param options - Options for the generator * @returns */ export async function createTypingsGeneratorAsync( taskSession: IHeftTaskSession, - heftConfiguration: HeftConfiguration, options: IStaticAssetGeneratorOptions ): Promise { const { tryGetConfigAsync, slashNormalizedBuildFolderPath } = options; const { terminal } = taskSession.logger; - const configurationJson: IStaticAssetTypingsConfigurationJson | undefined = await tryGetConfigAsync( - terminal, - slashNormalizedBuildFolderPath, - heftConfiguration.rigConfig - ); + const configuration: IBinaryStaticAssetTypingsConfigurationJson | undefined = + await tryGetConfigAsync(terminal); - if (!configurationJson) { + if (!configuration) { return false; } - const secondaryGeneratedTsFolders: string[] | undefined = - configurationJson.secondaryGeneratedTsFolders?.map( - (folder) => `${slashNormalizedBuildFolderPath}/${folder}` - ); - - const { generatedTsFolder = 'temp/static-asset-ts', sourceFolderPath = 'src' } = configurationJson; - - const configuration: IStaticAssetTypingsConfiguration = { - ...configurationJson, - srcFolder: `${slashNormalizedBuildFolderPath}/${sourceFolderPath}`, - generatedTsFolder: `${slashNormalizedBuildFolderPath}/${generatedTsFolder}`, - secondaryGeneratedTsFolders - }; + const { + generatedTsFolders = ['temp/static-asset-ts'], + sourceFolderPath = 'src', + fileExtensions + } = configuration; + const resolvedGeneratedTsFolders: string[] | undefined = generatedTsFolders.map( + (folder) => `${slashNormalizedBuildFolderPath}/${folder}` + ); + const [generatedTsFolder, ...secondaryGeneratedTsFolders] = resolvedGeneratedTsFolders; const { getAdditionalOutputFiles, getVersionAndEmitOutputFilesAsync } = options; const fileVersions: Map = new Map(); const typingsGenerator: TypingsGenerator = new TypingsGenerator({ - ...configuration, + srcFolder: `${slashNormalizedBuildFolderPath}/${sourceFolderPath}`, + generatedTsFolder: `${slashNormalizedBuildFolderPath}/${generatedTsFolder}`, + secondaryGeneratedTsFolders, + fileExtensions, terminal, // eslint-disable-next-line @typescript-eslint/naming-convention parseAndGenerateTypings: async ( diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/TextAssetsPlugin.ts b/heft-plugins/heft-static-asset-typings-plugin/src/TextAssetsPlugin.ts index 32ac1db5c98..51dbce8b718 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/TextAssetsPlugin.ts +++ b/heft-plugins/heft-static-asset-typings-plugin/src/TextAssetsPlugin.ts @@ -4,125 +4,128 @@ import { createHash } from 'node:crypto'; import type { HeftConfiguration, IHeftTaskSession, IHeftTaskPlugin } from '@rushstack/heft'; -import { FileSystem, Path } from '@rushstack/node-core-library'; -import type { ITerminal } from '@rushstack/terminal'; +import { FileSystem } from '@rushstack/node-core-library'; import { createTypingsGeneratorAsync, + tryGetConfigFromPluginOptionsAsync, type IRunGeneratorOptions, type IStaticAssetGeneratorOptions, type IStaticAssetTypingsGenerator } from './StaticAssetTypingsGenerator'; -import { getConfigFromConfigFileAsync } from './getConfigFromConfigFileAsync'; -import type { IStaticAssetTypingsConfigurationJson } from './types'; +import type { IAssetPluginOptions, ITextStaticAssetTypingsConfigurationJson } from './types'; const PLUGIN_NAME: 'text-assets-plugin' = 'text-assets-plugin'; -export interface ITextAssetsPluginOptions { - cjsOutputFolders: string[]; - esmOutputFolders: string[]; - configFileName?: string; -} - -export default class TextAssetsPlugin implements IHeftTaskPlugin { +export default class TextAssetsPlugin + implements IHeftTaskPlugin> +{ /** * Generate typings for text files before TypeScript compilation. */ public apply( taskSession: IHeftTaskSession, heftConfiguration: HeftConfiguration, - options: ITextAssetsPluginOptions + pluginOptions: IAssetPluginOptions ): void { - const slashNormalizedBuildFolderPath: string = Path.convertToSlashes(heftConfiguration.buildFolderPath); - - const cjsOutputFolders: string[] = options.cjsOutputFolders.map( - (jsPath) => `${slashNormalizedBuildFolderPath}/${jsPath}` - ); - const esmOutputFolders: string[] = options.esmOutputFolders.map( - (jsPath) => `${slashNormalizedBuildFolderPath}/${jsPath}` - ); - const jsOutputFolders: string[] = [...cjsOutputFolders, ...esmOutputFolders]; - - const { configFileName = 'text-assets.json' } = options; - - function getAdditionalOutputFiles(relativePath: string): string[] { - return jsOutputFolders.map((folder) => `${folder}/${relativePath}.js`); - } - - async function tryGetConfigAsync( - terminal: ITerminal, - buildFolderPath: string, - rigConfig: HeftConfiguration['rigConfig'] - ): Promise { - return getConfigFromConfigFileAsync(configFileName, terminal, buildFolderPath, rigConfig); - } - - async function getVersionAndEmitOutputFilesAsync( - filePath: string, - relativePath: string, - oldVersion: string | undefined - ): Promise { - const fileContents: Buffer = await FileSystem.readFileToBufferAsync(filePath); - const fileVersion: string = createHash('sha1').update(fileContents).digest('base64'); - if (fileVersion === oldVersion) { - return; - } - - const stringFileContents: string = fileContents.toString('utf8'); - - const stringifiedContents: string = JSON.stringify(stringFileContents); - - const outputs: Map = new Map(); - if (cjsOutputFolders.length) { - const outputSource: string = [ - `"use strict"`, - `Object.defineProperty(exports, "__esModule", { value: true });`, - `var content = ${stringifiedContents};`, - `exports.default = content;` - ].join('\n'); - const outputBuffer: Buffer = Buffer.from(outputSource); - for (const folder of cjsOutputFolders) { - outputs.set(`${folder}/${relativePath}.js`, outputBuffer); - } - } - - if (esmOutputFolders.length) { - const outputSource: string = [ - `const content = ${stringifiedContents};`, - `export default content;` - ].join('\n'); - const outputBuffer: Buffer = Buffer.from(outputSource); - for (const folder of esmOutputFolders) { - outputs.set(`${folder}/${relativePath}.js`, outputBuffer); - } - } - - await Promise.all( - Array.from(outputs, ([outputPath, outputBuffer]) => - FileSystem.writeFileAsync(outputPath, outputBuffer, { ensureFolderExists: true }) - ) - ); - - return fileVersion; - } - - const staticAssetGeneratorOptions: IStaticAssetGeneratorOptions = { - tryGetConfigAsync, - slashNormalizedBuildFolderPath, - getAdditionalOutputFiles, - getVersionAndEmitOutputFilesAsync - }; - let generator: IStaticAssetTypingsGenerator | undefined | false; async function createAndRunGeneratorAsync(runOptions: IRunGeneratorOptions): Promise { if (generator === undefined) { - // eslint-disable-next-line require-atomic-updates - generator = await createTypingsGeneratorAsync( - taskSession, - heftConfiguration, - staticAssetGeneratorOptions - ); + const { slashNormalizedBuildFolderPath, rigConfig } = heftConfiguration; + + const options: ITextStaticAssetTypingsConfigurationJson | undefined = + await tryGetConfigFromPluginOptionsAsync( + taskSession.logger.terminal, + slashNormalizedBuildFolderPath, + rigConfig, + pluginOptions, + 'text' + ); + + if (options) { + const { fileExtensions, sourceFolderPath, generatedTsFolders, cjsOutputFolders, esmOutputFolders } = + options; + + const resolvedCjsOutputFolders: string[] = cjsOutputFolders.map( + (jsPath) => `${slashNormalizedBuildFolderPath}/${jsPath}` + ); + const resolvedEsmOutputFolders: string[] = + esmOutputFolders?.map((jsPath) => `${slashNormalizedBuildFolderPath}/${jsPath}`) ?? []; + const jsOutputFolders: string[] = [...resolvedCjsOutputFolders, ...resolvedEsmOutputFolders]; + + function getAdditionalOutputFiles(relativePath: string): string[] { + return jsOutputFolders.map((folder) => `${folder}/${relativePath}.js`); + } + + async function getVersionAndEmitOutputFilesAsync( + filePath: string, + relativePath: string, + oldVersion: string | undefined + ): Promise { + const fileContents: Buffer = await FileSystem.readFileToBufferAsync(filePath); + const fileVersion: string = createHash('sha1').update(fileContents).digest('base64'); + if (fileVersion === oldVersion) { + return; + } + + const stringFileContents: string = fileContents.toString('utf8'); + + const stringifiedContents: string = JSON.stringify(stringFileContents); + + const outputs: Map = new Map(); + if (resolvedCjsOutputFolders.length) { + const outputSource: string = [ + `"use strict"`, + `Object.defineProperty(exports, "__esModule", { value: true });`, + `var content = ${stringifiedContents};`, + `exports.default = content;` + ].join('\n'); + const outputBuffer: Buffer = Buffer.from(outputSource); + for (const folder of resolvedCjsOutputFolders) { + outputs.set(`${folder}/${relativePath}.js`, outputBuffer); + } + } + + if (resolvedEsmOutputFolders.length) { + const outputSource: string = [ + `const content = ${stringifiedContents};`, + `export default content;` + ].join('\n'); + const outputBuffer: Buffer = Buffer.from(outputSource); + for (const folder of resolvedEsmOutputFolders) { + outputs.set(`${folder}/${relativePath}.js`, outputBuffer); + } + } + + await Promise.all( + Array.from(outputs, ([outputPath, outputBuffer]) => + FileSystem.writeFileAsync(outputPath, outputBuffer, { ensureFolderExists: true }) + ) + ); + + return fileVersion; + } + + const staticAssetGeneratorOptions: IStaticAssetGeneratorOptions = { + tryGetConfigAsync: async () => { + return { + fileExtensions, + sourceFolderPath, + generatedTsFolders + }; + }, + slashNormalizedBuildFolderPath, + getAdditionalOutputFiles, + getVersionAndEmitOutputFilesAsync + }; + + // eslint-disable-next-line require-atomic-updates + generator = await createTypingsGeneratorAsync(taskSession, staticAssetGeneratorOptions); + } else { + // eslint-disable-next-line require-atomic-updates + generator = false; + } } if (generator === false) { diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/getConfigFromConfigFileAsync.ts b/heft-plugins/heft-static-asset-typings-plugin/src/getConfigFromConfigFileAsync.ts index 44a20e897e4..1b7e4e10343 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/getConfigFromConfigFileAsync.ts +++ b/heft-plugins/heft-static-asset-typings-plugin/src/getConfigFromConfigFileAsync.ts @@ -2,22 +2,35 @@ // See LICENSE in the project root for license information. import type { HeftConfiguration } from '@rushstack/heft'; -import { ConfigurationFile, InheritanceType } from '@rushstack/heft-config-file'; +import { InheritanceType, ProjectConfigurationFile } from '@rushstack/heft-config-file'; import type { ITerminal } from '@rushstack/terminal'; -import type { IStaticAssetTypingsConfigurationJson } from './types'; -import staticAssetSchema from './schemas/static-asset-typings.schema.json'; +import type { + IBinaryStaticAssetTypingsConfigurationJson, + ITextStaticAssetTypingsConfigurationJson +} from './types'; +import binaryStaticAssetSchema from './schemas/binary-assets-options.schema.json'; +import textStaticAssetSchema from './schemas/text-assets-options.schema.json'; const configurationFileLoaderByFileName: Map< string, - ConfigurationFile + ProjectConfigurationFile< + IBinaryStaticAssetTypingsConfigurationJson | ITextStaticAssetTypingsConfigurationJson + > > = new Map(); +export type FileLoaderType = 'binary' | 'text'; + function createConfigurationFileLoader( - configFileName: string -): ConfigurationFile { - return new ConfigurationFile({ - jsonSchemaObject: staticAssetSchema, + configFileName: string, + fileLoaderType: FileLoaderType +): ProjectConfigurationFile< + IBinaryStaticAssetTypingsConfigurationJson | ITextStaticAssetTypingsConfigurationJson +> { + return new ProjectConfigurationFile< + IBinaryStaticAssetTypingsConfigurationJson | ITextStaticAssetTypingsConfigurationJson + >({ + jsonSchemaObject: fileLoaderType === 'binary' ? binaryStaticAssetSchema : textStaticAssetSchema, projectRelativeFilePath: `config/${configFileName}`, propertyInheritance: { fileExtensions: { @@ -29,14 +42,20 @@ function createConfigurationFileLoader( export function getConfigFromConfigFileAsync( configFileName: string, + fileLoaderType: FileLoaderType, terminal: ITerminal, slashNormalizedBuildFolderPath: string, rigConfig: HeftConfiguration['rigConfig'] -): Promise { - let configurationFileLoader: ConfigurationFile | undefined = - configurationFileLoaderByFileName.get(configFileName); +): Promise< + IBinaryStaticAssetTypingsConfigurationJson | ITextStaticAssetTypingsConfigurationJson | undefined +> { + let configurationFileLoader: + | ProjectConfigurationFile< + IBinaryStaticAssetTypingsConfigurationJson | ITextStaticAssetTypingsConfigurationJson + > + | undefined = configurationFileLoaderByFileName.get(configFileName); if (!configurationFileLoader) { - configurationFileLoader = createConfigurationFileLoader(configFileName); + configurationFileLoader = createConfigurationFileLoader(configFileName, fileLoaderType); configurationFileLoaderByFileName.set(configFileName, configurationFileLoader); } diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/schemas/binary-assets-options.schema.json b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/binary-assets-options.schema.json index 64218a34e98..749715a9f63 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/schemas/binary-assets-options.schema.json +++ b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/binary-assets-options.schema.json @@ -16,32 +16,29 @@ "type": "object", "additionalProperties": false, "properties": { - "$schema": { - "type": "string" - }, "fileExtensions": { "type": "array", "items": { - "pattern": "\\.[^\\\\/]+$", - "type": "string" + "type": "string", + "pattern": "\\.[^\\\\/]+$" } }, - "generatedTsFolder": { - "type": "string" - }, - "secondaryGeneratedTsFolders": { + "generatedTsFolders": { "type": "array", "items": { - "type": "string" + "type": "string", + "pattern": "^[^\\\\]+$" } }, "sourceFolderPath": { - "type": "string" + "type": "string", + "pattern": "^[^\\\\]+$" } } } } }, + { "type": "object", "additionalProperties": false, diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/schemas/static-asset-typings.schema.json b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/binary-static-asset-typings.schema.json similarity index 75% rename from heft-plugins/heft-static-asset-typings-plugin/src/schemas/static-asset-typings.schema.json rename to heft-plugins/heft-static-asset-typings-plugin/src/schemas/binary-static-asset-typings.schema.json index 05c91a28aba..640f68eb4dd 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/schemas/static-asset-typings.schema.json +++ b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/binary-static-asset-typings.schema.json @@ -14,17 +14,16 @@ "type": "string" } }, - "generatedTsFolder": { - "type": "string" - }, - "secondaryGeneratedTsFolders": { + "generatedTsFolders": { "type": "array", "items": { - "type": "string" + "type": "string", + "pattern": "^[^\\\\]+$" } }, "sourceFolderPath": { - "type": "string" + "type": "string", + "pattern": "^[^\\\\]+$" } } } diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/schemas/test-asset-typings.schema.json b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/test-asset-typings.schema.json new file mode 100644 index 00000000000..482c5181c87 --- /dev/null +++ b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/test-asset-typings.schema.json @@ -0,0 +1,43 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "additionalProperties": false, + "required": ["fileExtensions", "cjsOutputFolders"], + "properties": { + "$schema": { + "type": "string" + }, + "fileExtensions": { + "type": "array", + "items": { + "pattern": "\\.[^\\\\/]+$", + "type": "string" + } + }, + "generatedTsFolders": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[^\\\\]+$" + } + }, + "sourceFolderPath": { + "type": "string", + "pattern": "^[^\\\\]+$" + }, + "cjsOutputFolders": { + "type": "array", + "items": { + "pattern": "^[^\\\\]+$", + "type": "string" + } + }, + "esmOutputFolders": { + "type": "array", + "items": { + "pattern": "^[^\\\\]+$", + "type": "string" + } + } + } +} diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/schemas/text-assets-options.schema.json b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/text-assets-options.schema.json index b1c7affd539..6dd4d724767 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/schemas/text-assets-options.schema.json +++ b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/text-assets-options.schema.json @@ -1,29 +1,72 @@ { "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", - "additionalProperties": false, - "required": ["cjsOutputFolders"], - "properties": { - "$schema": { - "type": "string" - }, - "cjsOutputFolders": { - "type": "array", - "items": { - "pattern": "^[^\\\\]+$", - "type": "string" + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "required": ["configType", "config"], + "properties": { + "configType": { + "type": "string", + "enum": ["inline"] + }, + "config": { + "required": ["fileExtensions", "cjsOutputFolders"], + "type": "object", + "additionalProperties": false, + "properties": { + "fileExtensions": { + "type": "array", + "items": { + "type": "string", + "pattern": "\\.[^\\\\/]+$" + } + }, + "generatedTsFolders": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[^\\\\]+$" + } + }, + "sourceFolderPath": { + "type": "string", + "pattern": "^[^\\\\]+$" + }, + "cjsOutputFolders": { + "type": "array", + "items": { + "pattern": "^[^\\\\]+$", + "type": "string" + } + }, + "esmOutputFolders": { + "type": "array", + "items": { + "pattern": "^[^\\\\]+$", + "type": "string" + } + } + } + } } }, - "esmOutputFolders": { - "type": "array", - "items": { - "pattern": "^[^\\\\]+$", - "type": "string" + + { + "type": "object", + "additionalProperties": false, + "required": ["configType", "configFileName"], + "properties": { + "configType": { + "type": "string", + "enum": ["file"] + }, + "configFileName": { + "type": "string", + "pattern": "^[^\\\\\\/]+\\.json$" + } } - }, - "configFileName": { - "type": "string", - "pattern": "^[^\\\\\\/]+\\.json$" } - } + ] } diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/types.ts b/heft-plugins/heft-static-asset-typings-plugin/src/types.ts index eefbbd80379..32db8776635 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/types.ts +++ b/heft-plugins/heft-static-asset-typings-plugin/src/types.ts @@ -1,18 +1,41 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import type { HeftConfiguration } from '@rushstack/heft'; import type { ITerminal } from '@rushstack/terminal'; -export interface IStaticAssetTypingsConfigurationJson { +export interface IAssetsInlineConfigPluginOptionsBase< + TConfig extends IBinaryStaticAssetTypingsConfigurationJson +> { + configType: 'inline'; + /** + * The inline configuration object. + */ + config: TConfig; +} + +export interface IAssetsFileConfigPluginOptions { + configType: 'file'; + /** + * The name of the riggable config file in the config/ folder. + */ + configFileName: string; +} + +export type IAssetPluginOptions = + | IAssetsInlineConfigPluginOptionsBase + | IAssetsFileConfigPluginOptions; + +export interface IBinaryStaticAssetTypingsConfigurationJson { fileExtensions: string[]; - generatedTsFolder?: string; - secondaryGeneratedTsFolders?: string[]; + generatedTsFolders?: string[]; sourceFolderPath?: string; } +export interface ITextStaticAssetTypingsConfigurationJson extends IBinaryStaticAssetTypingsConfigurationJson { + cjsOutputFolders: string[]; + esmOutputFolders?: string[]; +} + export type StaticAssetConfigurationFileLoader = ( - terminal: ITerminal, - slashNormalizedBuildFolderPath: string, - rigConfig: HeftConfiguration['rigConfig'] -) => Promise; + terminal: ITerminal +) => Promise; From eb15e76ee1efd40ecb26fa2f0757c7d199be1e77 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Mon, 23 Feb 2026 15:55:07 -0800 Subject: [PATCH 12/17] fixup! Use the new plugin in build-tests projects. --- build-tests/heft-node-everything-test/config/jest.config.json | 1 - 1 file changed, 1 deletion(-) diff --git a/build-tests/heft-node-everything-test/config/jest.config.json b/build-tests/heft-node-everything-test/config/jest.config.json index 8003417cece..d22ada97183 100644 --- a/build-tests/heft-node-everything-test/config/jest.config.json +++ b/build-tests/heft-node-everything-test/config/jest.config.json @@ -1,7 +1,6 @@ { "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json", - // The project outputs to lib-commonjs, not lib "roots": ["/lib-commonjs"], "testMatch": ["/lib-commonjs/**/*.test.{cjs,js}"], From 7e1fe2ae2d227577a928115cc30471b8b948eb4f Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Mon, 23 Feb 2026 16:12:34 -0800 Subject: [PATCH 13/17] fixup! Rationalzie the plugin options. --- .../config/heft.json | 11 ++ .../config/text-assets.json | 6 + .../package.json | 1 + .../src/exampleTemplate.html | 1 + .../src/index.ts | 8 + .../tsconfig.json | 2 +- .../config/heft.json | 9 +- .../config/heft.json | 2 +- .../config/binary-assets.json | 4 + .../config/heft.json | 7 +- .../config/heft.json | 2 +- .../config/subspaces/default/pnpm-lock.yaml | 3 + .../README.md | 147 ++++++++++++++---- .../src/getConfigFromConfigFileAsync.ts | 4 +- .../profiles/app/config/heft.json | 2 +- .../profiles/library/config/heft.json | 2 +- 16 files changed, 163 insertions(+), 48 deletions(-) create mode 100644 build-tests/heft-node-everything-esm-module-test/config/text-assets.json create mode 100644 build-tests/heft-node-everything-esm-module-test/src/exampleTemplate.html create mode 100644 build-tests/heft-webpack4-everything-test/config/binary-assets.json diff --git a/build-tests/heft-node-everything-esm-module-test/config/heft.json b/build-tests/heft-node-everything-esm-module-test/config/heft.json index bfb4f25a904..7893405e35d 100644 --- a/build-tests/heft-node-everything-esm-module-test/config/heft.json +++ b/build-tests/heft-node-everything-esm-module-test/config/heft.json @@ -9,7 +9,18 @@ "cleanFiles": [{ "includeGlobs": ["dist", "lib-commonjs", "lib", "lib-esm", "lib-esnext", "lib-umd"] }], "tasksByName": { + "text-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "text-assets-plugin", + "options": { + "configType": "file", + "configFileName": "text-assets.json" + } + } + }, "typescript": { + "taskDependencies": ["text-typings"], "taskPlugin": { "pluginPackage": "@rushstack/heft-typescript-plugin" } diff --git a/build-tests/heft-node-everything-esm-module-test/config/text-assets.json b/build-tests/heft-node-everything-esm-module-test/config/text-assets.json new file mode 100644 index 00000000000..7d3efa3121a --- /dev/null +++ b/build-tests/heft-node-everything-esm-module-test/config/text-assets.json @@ -0,0 +1,6 @@ +{ + "fileExtensions": [".html"], + "cjsOutputFolders": ["lib-commonjs"], + "esmOutputFolders": ["lib-esm"], + "generatedTsFolders": ["temp/text-typings"] +} diff --git a/build-tests/heft-node-everything-esm-module-test/package.json b/build-tests/heft-node-everything-esm-module-test/package.json index 0f4196f00ab..cffaca35a45 100644 --- a/build-tests/heft-node-everything-esm-module-test/package.json +++ b/build-tests/heft-node-everything-esm-module-test/package.json @@ -17,6 +17,7 @@ "@rushstack/heft-api-extractor-plugin": "workspace:*", "@rushstack/heft-jest-plugin": "workspace:*", "@rushstack/heft-lint-plugin": "workspace:*", + "@rushstack/heft-static-asset-typings-plugin": "workspace:*", "@rushstack/heft-typescript-plugin": "workspace:*", "@types/heft-jest": "1.0.1", "@types/node": "20.17.19", diff --git a/build-tests/heft-node-everything-esm-module-test/src/exampleTemplate.html b/build-tests/heft-node-everything-esm-module-test/src/exampleTemplate.html new file mode 100644 index 00000000000..44ab8a9ae74 --- /dev/null +++ b/build-tests/heft-node-everything-esm-module-test/src/exampleTemplate.html @@ -0,0 +1 @@ +

This is an example template imported as a text asset.

diff --git a/build-tests/heft-node-everything-esm-module-test/src/index.ts b/build-tests/heft-node-everything-esm-module-test/src/index.ts index 659610ef84f..22b50eb31b1 100644 --- a/build-tests/heft-node-everything-esm-module-test/src/index.ts +++ b/build-tests/heft-node-everything-esm-module-test/src/index.ts @@ -1,6 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import exampleTemplate from './exampleTemplate.html'; + +/** + * The content of exampleTemplate.html, imported as a text asset. + * @public + */ +export const templateContent: string = exampleTemplate; + /** * @public */ diff --git a/build-tests/heft-node-everything-esm-module-test/tsconfig.json b/build-tests/heft-node-everything-esm-module-test/tsconfig.json index d08637a6a20..550c1aa1ab3 100644 --- a/build-tests/heft-node-everything-esm-module-test/tsconfig.json +++ b/build-tests/heft-node-everything-esm-module-test/tsconfig.json @@ -4,7 +4,7 @@ "compilerOptions": { "outDir": "lib-commonjs", "declarationDir": "lib-dts", - "rootDir": "src", + "rootDirs": ["src", "temp/text-typings"], "forceConsistentCasingInFileNames": true, "jsx": "react", diff --git a/build-tests/heft-node-everything-test/config/heft.json b/build-tests/heft-node-everything-test/config/heft.json index 63e653cf8ff..9ff31460373 100644 --- a/build-tests/heft-node-everything-test/config/heft.json +++ b/build-tests/heft-node-everything-test/config/heft.json @@ -21,8 +21,13 @@ "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", "pluginName": "text-assets-plugin", "options": { - "cjsOutputFolders": ["lib-commonjs"], - "esmOutputFolders": ["lib-esm"] + "configType": "inline", + "config": { + "fileExtensions": [".html"], + "cjsOutputFolders": ["lib-commonjs"], + "esmOutputFolders": ["lib-esm"], + "generatedTsFolders": ["temp/text-typings"] + } } } }, diff --git a/build-tests/heft-rspack-everything-test/config/heft.json b/build-tests/heft-rspack-everything-test/config/heft.json index 76d98cebe77..757dacbf9be 100644 --- a/build-tests/heft-rspack-everything-test/config/heft.json +++ b/build-tests/heft-rspack-everything-test/config/heft.json @@ -18,7 +18,7 @@ "configType": "inline", "config": { "fileExtensions": [".png"], - "generatedTsFolder": "temp/image-typings" + "generatedTsFolders": ["temp/image-typings"] } } } diff --git a/build-tests/heft-webpack4-everything-test/config/binary-assets.json b/build-tests/heft-webpack4-everything-test/config/binary-assets.json new file mode 100644 index 00000000000..09dfe4998d3 --- /dev/null +++ b/build-tests/heft-webpack4-everything-test/config/binary-assets.json @@ -0,0 +1,4 @@ +{ + "fileExtensions": [".png"], + "generatedTsFolders": ["temp/image-typings"] +} diff --git a/build-tests/heft-webpack4-everything-test/config/heft.json b/build-tests/heft-webpack4-everything-test/config/heft.json index 3cd0d0b48c9..29914a13d13 100644 --- a/build-tests/heft-webpack4-everything-test/config/heft.json +++ b/build-tests/heft-webpack4-everything-test/config/heft.json @@ -15,11 +15,8 @@ "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", "pluginName": "binary-assets-plugin", "options": { - "configType": "inline", - "config": { - "fileExtensions": [".png"], - "generatedTsFolder": "temp/image-typings" - } + "configType": "file", + "configFileName": "binary-assets.json" } } }, diff --git a/build-tests/heft-webpack5-everything-test/config/heft.json b/build-tests/heft-webpack5-everything-test/config/heft.json index 944fe7efd56..dd557c18dd7 100644 --- a/build-tests/heft-webpack5-everything-test/config/heft.json +++ b/build-tests/heft-webpack5-everything-test/config/heft.json @@ -18,7 +18,7 @@ "configType": "inline", "config": { "fileExtensions": [".png"], - "generatedTsFolder": "temp/image-typings" + "generatedTsFolders": ["temp/image-typings"] } } } diff --git a/common/config/subspaces/default/pnpm-lock.yaml b/common/config/subspaces/default/pnpm-lock.yaml index c55d9cfd060..da03c0d6e70 100644 --- a/common/config/subspaces/default/pnpm-lock.yaml +++ b/common/config/subspaces/default/pnpm-lock.yaml @@ -1950,6 +1950,9 @@ importers: '@rushstack/heft-lint-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-lint-plugin + '@rushstack/heft-static-asset-typings-plugin': + specifier: workspace:* + version: link:../../heft-plugins/heft-static-asset-typings-plugin '@rushstack/heft-typescript-plugin': specifier: workspace:* version: link:../../heft-plugins/heft-typescript-plugin diff --git a/heft-plugins/heft-static-asset-typings-plugin/README.md b/heft-plugins/heft-static-asset-typings-plugin/README.md index 749bb997a94..c2a2ac044e7 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/README.md +++ b/heft-plugins/heft-static-asset-typings-plugin/README.md @@ -25,6 +25,8 @@ Both plugins support incremental and watch-mode builds. ### Binary assets (images, fonts, etc.) + **Inline configuration** — specify options directly in heft.json: + ```jsonc { "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", @@ -39,7 +41,7 @@ Both plugins support incremental and watch-mode builds. "configType": "inline", "config": { "fileExtensions": [".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp", ".avif"], - "generatedTsFolder": "temp/image-typings" + "generatedTsFolders": ["temp/image-typings"] } } } @@ -54,27 +56,98 @@ Both plugins support incremental and watch-mode builds. } ``` + **File configuration** — load settings from a riggable config file: + + ```jsonc + { + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "phasesByName": { + "build": { + "tasksByName": { + "image-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "binary-assets-plugin", + "options": { + "configType": "file", + "configFileName": "binary-assets.json" + } + } + }, + "typescript": { + "taskDependencies": ["image-typings"] + // ... + } + } + } + } + } + ``` + + And create a **config/binary-assets.json** file (which can be provided by a rig): + + ```jsonc + { + "fileExtensions": [".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp", ".avif"], + "generatedTsFolders": ["temp/image-typings"] + } + ``` + ### Text assets + **Inline configuration:** + + ```jsonc + { + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + "phasesByName": { + "build": { + "tasksByName": { + "text-typings": { + "taskPlugin": { + "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", + "pluginName": "text-assets-plugin", + "options": { + "configType": "inline", + "config": { + "fileExtensions": [".html"], + "cjsOutputFolders": ["lib-commonjs"], + "esmOutputFolders": ["lib-esm"], + "generatedTsFolders": ["temp/text-typings"] + } + } + } + }, + "typescript": { + "taskDependencies": ["text-typings"] + // ... + } + } + } + } + } + ``` + + **File configuration:** + ```jsonc { "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", "phasesByName": { "build": { "tasksByName": { - "text-assets": { + "text-typings": { "taskPlugin": { "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", "pluginName": "text-assets-plugin", "options": { - "cjsOutputFolders": ["lib-commonjs"], - "esmOutputFolders": ["lib-esm"] - // "configFileName": "text-assets.json" // (optional, name of riggable config file) + "configType": "file", + "configFileName": "text-assets.json" } } }, "typescript": { - "taskDependencies": ["text-assets"] + "taskDependencies": ["text-typings"] // ... } } @@ -83,6 +156,17 @@ Both plugins support incremental and watch-mode builds. } ``` + And create a **config/text-assets.json** file (which can be provided by a rig): + + ```jsonc + { + "fileExtensions": [".html"], + "cjsOutputFolders": ["lib-commonjs"], + "esmOutputFolders": ["lib-esm"], + "generatedTsFolders": ["temp/text-typings"] + } + ``` + 3. Add the generated typings folder to your **tsconfig.json** `rootDirs` so that TypeScript can resolve the declarations: @@ -96,44 +180,39 @@ Both plugins support incremental and watch-mode builds. ## Plugin options -### `binary-assets-plugin` +Both plugins support two configuration modes via the `configType` option: -Supports two configuration modes: +### Inline mode (`configType: "inline"`) -**Inline mode** (`configType: "inline"`): +Provide configuration directly in heft.json under `options.config`: -| Option | Type | Default | Description | -| ---------------------------- | ---------- | --------- | -------------------------------------------------------------------- | -| `config.fileExtensions` | `string[]` | — | **(required)** File extensions to generate typings for. | -| `config.generatedTsFolder` | `string` | `"temp/static-asset-typings"` | Output folder for the generated `.d.ts` files. | -| `config.secondaryGeneratedTsFolders` | `string[]` | `[]` | Additional output folders for generated `.d.ts` files. | -| `config.sourceFolderPath` | `string` | `"src"` | Source folder to scan for asset files. | +#### `binary-assets-plugin` inline config -**File mode** (`configType: "file"`): +| Option | Type | Default | Description | +| ------------------- | ---------- | ------------------------ | ----------------------------------------------- | +| `fileExtensions` | `string[]` | — | **(required)** File extensions to generate typings for. | +| `generatedTsFolders`| `string[]` | `["temp/static-asset-ts"]` | Output folders for the generated `.d.ts` files. | +| `sourceFolderPath` | `string` | `"src"` | Source folder to scan for asset files. | -| Option | Type | Description | -| ---------------- | -------- | ---------------------------------------------------------------------- | -| `configFileName` | `string` | **(required)** Name of a riggable JSON config file in the `config/` folder. | +#### `text-assets-plugin` inline config + +Includes all the above, plus: -### `text-assets-plugin` +| Option | Type | Default | Description | +| ------------------- | ---------- | ------------------------ | ---------------------------------------------------- | +| `cjsOutputFolders` | `string[]` | — | **(required)** Output folders for generated CommonJS `.js` modules. | +| `esmOutputFolders` | `string[]` | `[]` | Output folders for generated ESM `.js` modules. | -| Option | Type | Default | Description | -| ---------------- | ---------- | -------------------- | ---------------------------------------------------------------- | -| `cjsOutputFolders` | `string[]` | — | **(required)** Output folders for generated CommonJS `.js` modules. | -| `esmOutputFolders` | `string[]` | `[]` | Output folders for generated ESM `.js` modules. | -| `configFileName` | `string` | `"text-assets.json"` | Name of a riggable JSON config file in the `config/` folder. | +### File mode (`configType: "file"`) -## Riggable config file format +Load configuration from a riggable JSON config file in the project's `config/` folder: -Both plugins can load configuration from a riggable JSON config file (located in your project's -`config/` folder). The schema supports the following properties: +| Option | Type | Description | +| ---------------- | -------- | ---------------------------------------------------------------------- | +| `configFileName` | `string` | **(required)** Name of the JSON config file in the `config/` folder. | -| Property | Type | Default | Description | -| ---------------------------- | ---------- | --------- | -------------------------------------------------------------- | -| `fileExtensions` | `string[]` | — | **(required)** File extensions to process. | -| `generatedTsFolder` | `string` | `"temp/static-asset-typings"` | Output folder for generated `.d.ts` typings. | -| `secondaryGeneratedTsFolders`| `string[]` | `[]` | Additional output folders for generated typings. | -| `sourceFolderPath` | `string` | `"src"` | Source folder to scan for matching files. | +The config file supports the same properties as inline mode (see tables above). Config files +can be provided by a rig, making file mode ideal for shared build configurations. ## Links diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/getConfigFromConfigFileAsync.ts b/heft-plugins/heft-static-asset-typings-plugin/src/getConfigFromConfigFileAsync.ts index 1b7e4e10343..b28dd758f40 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/getConfigFromConfigFileAsync.ts +++ b/heft-plugins/heft-static-asset-typings-plugin/src/getConfigFromConfigFileAsync.ts @@ -9,8 +9,8 @@ import type { IBinaryStaticAssetTypingsConfigurationJson, ITextStaticAssetTypingsConfigurationJson } from './types'; -import binaryStaticAssetSchema from './schemas/binary-assets-options.schema.json'; -import textStaticAssetSchema from './schemas/text-assets-options.schema.json'; +import binaryStaticAssetSchema from './schemas/binary-static-asset-typings.schema.json'; +import textStaticAssetSchema from './schemas/test-asset-typings.schema.json'; const configurationFileLoaderByFileName: Map< string, diff --git a/rigs/heft-web-rig/profiles/app/config/heft.json b/rigs/heft-web-rig/profiles/app/config/heft.json index 6e455df82d0..09ac1eabff2 100644 --- a/rigs/heft-web-rig/profiles/app/config/heft.json +++ b/rigs/heft-web-rig/profiles/app/config/heft.json @@ -56,7 +56,7 @@ "configType": "inline", "config": { "fileExtensions": [".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp", ".avif"], - "generatedTsFolder": "temp/image-typings" + "generatedTsFolders": ["temp/image-typings"] } } } diff --git a/rigs/heft-web-rig/profiles/library/config/heft.json b/rigs/heft-web-rig/profiles/library/config/heft.json index 6e455df82d0..09ac1eabff2 100644 --- a/rigs/heft-web-rig/profiles/library/config/heft.json +++ b/rigs/heft-web-rig/profiles/library/config/heft.json @@ -56,7 +56,7 @@ "configType": "inline", "config": { "fileExtensions": [".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp", ".avif"], - "generatedTsFolder": "temp/image-typings" + "generatedTsFolders": ["temp/image-typings"] } } } From 48d98705a25c1cd5b57991add43636710a0d72bd Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Mon, 23 Feb 2026 16:15:43 -0800 Subject: [PATCH 14/17] PR feedback. --- .../README.md | 2 +- .../src/BinaryAssetsPlugin.ts | 8 +- .../src/StaticAssetTypingsGenerator.ts | 22 ++- .../src/TextAssetsPlugin.ts | 185 +++++++++--------- 4 files changed, 111 insertions(+), 106 deletions(-) diff --git a/heft-plugins/heft-static-asset-typings-plugin/README.md b/heft-plugins/heft-static-asset-typings-plugin/README.md index c2a2ac044e7..6f814e59139 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/README.md +++ b/heft-plugins/heft-static-asset-typings-plugin/README.md @@ -191,7 +191,7 @@ Provide configuration directly in heft.json under `options.config`: | Option | Type | Default | Description | | ------------------- | ---------- | ------------------------ | ----------------------------------------------- | | `fileExtensions` | `string[]` | — | **(required)** File extensions to generate typings for. | -| `generatedTsFolders`| `string[]` | `["temp/static-asset-ts"]` | Output folders for the generated `.d.ts` files. | +| `generatedTsFolders`| `string[]` | `["temp/static-asset-ts"]` | Folders where generated `.d.ts` files are written. The first entry should be listed in `rootDirs` so TypeScript can resolve the asset imports during type-checking. Additional entries are typically your project's published typings folder(s). | | `sourceFolderPath` | `string` | `"src"` | Source folder to scan for asset files. | #### `text-assets-plugin` inline config diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/BinaryAssetsPlugin.ts b/heft-plugins/heft-static-asset-typings-plugin/src/BinaryAssetsPlugin.ts index 219aae3793d..0d19820a3bd 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/BinaryAssetsPlugin.ts +++ b/heft-plugins/heft-static-asset-typings-plugin/src/BinaryAssetsPlugin.ts @@ -40,14 +40,14 @@ export default class BinaryAssetsPlugin getVersionAndEmitOutputFilesAsync: async () => 'versionless' }; - let generator: IStaticAssetTypingsGenerator | undefined | false; + let generatorPromise: Promise | undefined; async function createAndRunGeneratorAsync(runOptions: IRunGeneratorOptions): Promise { - if (generator === undefined) { - // eslint-disable-next-line require-atomic-updates - generator = await createTypingsGeneratorAsync(taskSession, staticAssetGeneratorOptions); + if (generatorPromise === undefined) { + generatorPromise = createTypingsGeneratorAsync(taskSession, staticAssetGeneratorOptions); } + const generator: IStaticAssetTypingsGenerator | false = await generatorPromise; if (generator === false) { return; } diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts b/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts index 1d480e53ff1..bb7bebbeef9 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts +++ b/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import { createHash } from 'node:crypto'; + import type { HeftConfiguration, IHeftTaskRunHookOptions, @@ -19,14 +21,18 @@ import type { StaticAssetConfigurationFileLoader } from './types'; -const DECLARATION: string = `/** - * @public - */ -declare const content: string; -export default content; -`; +// Use explicit \n to avoid platform-dependent line endings in template literals. +const DECLARATION: string = [ + '/**', + ' * @public', + ' */', + 'declare const content: string;', + 'export default content;', + '' +].join('\n'); -const PLUGIN_VERSION: number = 1; +// Include a hash of DECLARATION so the cache is invalidated if the declaration content changes. +const PLUGIN_VERSION: string = `1-${createHash('sha1').update(DECLARATION).digest('hex').slice(0, 8)}`; /** * Options for constructing a static asset typings generator @@ -76,7 +82,7 @@ export interface IStaticAssetTypingsGenerator { interface IStaticAssetTypingsBuildInfoFile { fileVersions: [string, string][]; - pluginVersion: number; + pluginVersion: string; } export async function tryGetConfigFromPluginOptionsAsync( diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/TextAssetsPlugin.ts b/heft-plugins/heft-static-asset-typings-plugin/src/TextAssetsPlugin.ts index 51dbce8b718..e755f978fff 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/TextAssetsPlugin.ts +++ b/heft-plugins/heft-static-asset-typings-plugin/src/TextAssetsPlugin.ts @@ -4,7 +4,7 @@ import { createHash } from 'node:crypto'; import type { HeftConfiguration, IHeftTaskSession, IHeftTaskPlugin } from '@rushstack/heft'; -import { FileSystem } from '@rushstack/node-core-library'; +import { Async, FileSystem } from '@rushstack/node-core-library'; import { createTypingsGeneratorAsync, @@ -17,6 +17,15 @@ import type { IAssetPluginOptions, ITextStaticAssetTypingsConfigurationJson } fr const PLUGIN_NAME: 'text-assets-plugin' = 'text-assets-plugin'; +// Pre-allocated preamble/postamble buffers to avoid repeated allocations. +// Used with FileSystem.writeBuffersToFileAsync (writev) for efficient output. +const CJS_PREAMBLE: Buffer = Buffer.from( + '"use strict"\nObject.defineProperty(exports, "__esModule", { value: true });\nvar content = ' +); +const CJS_POSTAMBLE: Buffer = Buffer.from(';\nexports.default = content;\n'); +const ESM_PREAMBLE: Buffer = Buffer.from('const content = '); +const ESM_POSTAMBLE: Buffer = Buffer.from(';\nexport default content;\n'); + export default class TextAssetsPlugin implements IHeftTaskPlugin> { @@ -28,106 +37,96 @@ export default class TextAssetsPlugin heftConfiguration: HeftConfiguration, pluginOptions: IAssetPluginOptions ): void { - let generator: IStaticAssetTypingsGenerator | undefined | false; + let generatorPromise: Promise | undefined; + + async function initializeGeneratorAsync(): Promise { + const { slashNormalizedBuildFolderPath, rigConfig } = heftConfiguration; + + const options: ITextStaticAssetTypingsConfigurationJson | undefined = + await tryGetConfigFromPluginOptionsAsync( + taskSession.logger.terminal, + slashNormalizedBuildFolderPath, + rigConfig, + pluginOptions, + 'text' + ); + + if (options) { + const { fileExtensions, sourceFolderPath, generatedTsFolders, cjsOutputFolders, esmOutputFolders } = + options; + + const resolvedCjsOutputFolders: string[] = cjsOutputFolders.map( + (jsPath) => `${slashNormalizedBuildFolderPath}/${jsPath}` + ); + const resolvedEsmOutputFolders: string[] = + esmOutputFolders?.map((jsPath) => `${slashNormalizedBuildFolderPath}/${jsPath}`) ?? []; + const jsOutputFolders: string[] = [...resolvedCjsOutputFolders, ...resolvedEsmOutputFolders]; + + function getAdditionalOutputFiles(relativePath: string): string[] { + return jsOutputFolders.map((folder) => `${folder}/${relativePath}.js`); + } - async function createAndRunGeneratorAsync(runOptions: IRunGeneratorOptions): Promise { - if (generator === undefined) { - const { slashNormalizedBuildFolderPath, rigConfig } = heftConfiguration; - - const options: ITextStaticAssetTypingsConfigurationJson | undefined = - await tryGetConfigFromPluginOptionsAsync( - taskSession.logger.terminal, - slashNormalizedBuildFolderPath, - rigConfig, - pluginOptions, - 'text' - ); - - if (options) { - const { fileExtensions, sourceFolderPath, generatedTsFolders, cjsOutputFolders, esmOutputFolders } = - options; - - const resolvedCjsOutputFolders: string[] = cjsOutputFolders.map( - (jsPath) => `${slashNormalizedBuildFolderPath}/${jsPath}` - ); - const resolvedEsmOutputFolders: string[] = - esmOutputFolders?.map((jsPath) => `${slashNormalizedBuildFolderPath}/${jsPath}`) ?? []; - const jsOutputFolders: string[] = [...resolvedCjsOutputFolders, ...resolvedEsmOutputFolders]; - - function getAdditionalOutputFiles(relativePath: string): string[] { - return jsOutputFolders.map((folder) => `${folder}/${relativePath}.js`); + async function getVersionAndEmitOutputFilesAsync( + filePath: string, + relativePath: string, + oldVersion: string | undefined + ): Promise { + const fileContents: Buffer = await FileSystem.readFileToBufferAsync(filePath); + const fileVersion: string = createHash('sha1').update(fileContents).digest('base64'); + if (fileVersion === oldVersion) { + return; } - async function getVersionAndEmitOutputFilesAsync( - filePath: string, - relativePath: string, - oldVersion: string | undefined - ): Promise { - const fileContents: Buffer = await FileSystem.readFileToBufferAsync(filePath); - const fileVersion: string = createHash('sha1').update(fileContents).digest('base64'); - if (fileVersion === oldVersion) { - return; - } - - const stringFileContents: string = fileContents.toString('utf8'); - - const stringifiedContents: string = JSON.stringify(stringFileContents); - - const outputs: Map = new Map(); - if (resolvedCjsOutputFolders.length) { - const outputSource: string = [ - `"use strict"`, - `Object.defineProperty(exports, "__esModule", { value: true });`, - `var content = ${stringifiedContents};`, - `exports.default = content;` - ].join('\n'); - const outputBuffer: Buffer = Buffer.from(outputSource); - for (const folder of resolvedCjsOutputFolders) { - outputs.set(`${folder}/${relativePath}.js`, outputBuffer); - } - } - - if (resolvedEsmOutputFolders.length) { - const outputSource: string = [ - `const content = ${stringifiedContents};`, - `export default content;` - ].join('\n'); - const outputBuffer: Buffer = Buffer.from(outputSource); - for (const folder of resolvedEsmOutputFolders) { - outputs.set(`${folder}/${relativePath}.js`, outputBuffer); - } - } - - await Promise.all( - Array.from(outputs, ([outputPath, outputBuffer]) => - FileSystem.writeFileAsync(outputPath, outputBuffer, { ensureFolderExists: true }) - ) - ); - - return fileVersion; + const stringFileContents: string = fileContents.toString('utf8'); + + const contentBuffer: Buffer = Buffer.from(JSON.stringify(stringFileContents)); + + const outputs: { path: string; buffers: NodeJS.ArrayBufferView[] }[] = []; + for (const folder of resolvedCjsOutputFolders) { + outputs.push({ + path: `${folder}/${relativePath}.js`, + buffers: [CJS_PREAMBLE, contentBuffer, CJS_POSTAMBLE] + }); + } + for (const folder of resolvedEsmOutputFolders) { + outputs.push({ + path: `${folder}/${relativePath}.js`, + buffers: [ESM_PREAMBLE, contentBuffer, ESM_POSTAMBLE] + }); } - const staticAssetGeneratorOptions: IStaticAssetGeneratorOptions = { - tryGetConfigAsync: async () => { - return { - fileExtensions, - sourceFolderPath, - generatedTsFolders - }; - }, - slashNormalizedBuildFolderPath, - getAdditionalOutputFiles, - getVersionAndEmitOutputFilesAsync - }; - - // eslint-disable-next-line require-atomic-updates - generator = await createTypingsGeneratorAsync(taskSession, staticAssetGeneratorOptions); - } else { - // eslint-disable-next-line require-atomic-updates - generator = false; + await Async.forEachAsync(outputs, async ({ path, buffers }) => { + await FileSystem.writeBuffersToFileAsync(path, buffers, { ensureFolderExists: true }); + }); + + return fileVersion; } + + const staticAssetGeneratorOptions: IStaticAssetGeneratorOptions = { + tryGetConfigAsync: async () => { + return { + fileExtensions, + sourceFolderPath, + generatedTsFolders + }; + }, + slashNormalizedBuildFolderPath, + getAdditionalOutputFiles, + getVersionAndEmitOutputFilesAsync + }; + + return createTypingsGeneratorAsync(taskSession, staticAssetGeneratorOptions); + } else { + return false; + } + } + + async function createAndRunGeneratorAsync(runOptions: IRunGeneratorOptions): Promise { + if (generatorPromise === undefined) { + generatorPromise = initializeGeneratorAsync(); } + const generator: IStaticAssetTypingsGenerator | false = await generatorPromise; if (generator === false) { return; } From 04566f0f6f11e55739a30bc17d649095f218fc8d Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Mon, 23 Feb 2026 16:22:48 -0800 Subject: [PATCH 15/17] fixup! Rationalzie the plugin options. --- .../etc/heft-node-everything-esm-module-test.api.md | 3 +++ .../src/StaticAssetTypingsGenerator.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/build-tests/heft-node-everything-esm-module-test/etc/heft-node-everything-esm-module-test.api.md b/build-tests/heft-node-everything-esm-module-test/etc/heft-node-everything-esm-module-test.api.md index 9d5bceee423..86361b68da7 100644 --- a/build-tests/heft-node-everything-esm-module-test/etc/heft-node-everything-esm-module-test.api.md +++ b/build-tests/heft-node-everything-esm-module-test/etc/heft-node-everything-esm-module-test.api.md @@ -4,6 +4,9 @@ ```ts +// @public +export const templateContent: string; + // @public (undocumented) export class TestClass { } diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts b/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts index bb7bebbeef9..68b693c0fd1 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts +++ b/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts @@ -158,7 +158,7 @@ export async function createTypingsGeneratorAsync( const typingsGenerator: TypingsGenerator = new TypingsGenerator({ srcFolder: `${slashNormalizedBuildFolderPath}/${sourceFolderPath}`, - generatedTsFolder: `${slashNormalizedBuildFolderPath}/${generatedTsFolder}`, + generatedTsFolder, secondaryGeneratedTsFolders, fileExtensions, terminal, From de2da48ecb866685caa8791fe142dc62f7a811c7 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Mon, 23 Feb 2026 16:28:00 -0800 Subject: [PATCH 16/17] fixup! PR feedback. --- .../src/StaticAssetTypingsGenerator.ts | 3 +++ .../src/TextAssetsPlugin.ts | 10 +++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts b/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts index 68b693c0fd1..b36a869fb75 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts +++ b/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts @@ -192,6 +192,9 @@ export async function createTypingsGeneratorAsync( getAdditionalOutputFiles }); + // TODO: Heft has an internal incremental cache layer (IncrementalBuildInfo) used by built-in + // plugins like CopyFilesPlugin. It is not currently part of Heft's public API surface. If it + // becomes public, we should migrate to it instead of managing our own cache file. const cacheFilePath: string = `${taskSession.tempFolderPath}/static-assets.json`; try { const cacheFileContent: string = await FileSystem.readFileAsync(cacheFilePath); diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/TextAssetsPlugin.ts b/heft-plugins/heft-static-asset-typings-plugin/src/TextAssetsPlugin.ts index e755f978fff..7a39a9ecc84 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/TextAssetsPlugin.ts +++ b/heft-plugins/heft-static-asset-typings-plugin/src/TextAssetsPlugin.ts @@ -95,9 +95,13 @@ export default class TextAssetsPlugin }); } - await Async.forEachAsync(outputs, async ({ path, buffers }) => { - await FileSystem.writeBuffersToFileAsync(path, buffers, { ensureFolderExists: true }); - }); + await Async.forEachAsync( + outputs, + async ({ path, buffers }) => { + await FileSystem.writeBuffersToFileAsync(path, buffers, { ensureFolderExists: true }); + }, + { concurrency: 10 } + ); return fileVersion; } From 206a19ca76f406bb4bd7a950964a8ec5fca0da94 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Mon, 23 Feb 2026 17:22:37 -0800 Subject: [PATCH 17/17] Refactor binary and text to resource and source. --- .../config/heft.json | 4 +- .../{text-assets.json => source-assets.json} | 0 .../config/heft.json | 2 +- .../{text-assets.json => source-assets.json} | 0 .../config/heft.json | 2 +- .../config/heft.json | 4 +- ...inary-assets.json => resource-assets.json} | 0 .../config/heft.json | 2 +- .../README.md | 44 +++++++++++-------- .../heft-plugin.json | 12 ++--- ...ssetsPlugin.ts => ResourceAssetsPlugin.ts} | 10 ++--- ...tAssetsPlugin.ts => SourceAssetsPlugin.ts} | 14 +++--- .../src/StaticAssetTypingsGenerator.ts | 22 +++++----- .../src/getConfigFromConfigFileAsync.ts | 22 +++++----- ...on => resource-assets-options.schema.json} | 0 ...resource-static-asset-typings.schema.json} | 0 ...json => source-assets-options.schema.json} | 0 ...> source-static-asset-typings.schema.json} | 0 .../src/types.ts | 11 ++--- .../profiles/app/config/heft.json | 2 +- .../profiles/library/config/heft.json | 2 +- 21 files changed, 80 insertions(+), 73 deletions(-) rename build-tests/heft-node-everything-esm-module-test/config/{text-assets.json => source-assets.json} (100%) rename build-tests/heft-node-everything-test/config/{text-assets.json => source-assets.json} (100%) rename build-tests/heft-webpack4-everything-test/config/{binary-assets.json => resource-assets.json} (100%) rename heft-plugins/heft-static-asset-typings-plugin/src/{BinaryAssetsPlugin.ts => ResourceAssetsPlugin.ts} (85%) rename heft-plugins/heft-static-asset-typings-plugin/src/{TextAssetsPlugin.ts => SourceAssetsPlugin.ts} (91%) rename heft-plugins/heft-static-asset-typings-plugin/src/schemas/{binary-assets-options.schema.json => resource-assets-options.schema.json} (100%) rename heft-plugins/heft-static-asset-typings-plugin/src/schemas/{binary-static-asset-typings.schema.json => resource-static-asset-typings.schema.json} (100%) rename heft-plugins/heft-static-asset-typings-plugin/src/schemas/{text-assets-options.schema.json => source-assets-options.schema.json} (100%) rename heft-plugins/heft-static-asset-typings-plugin/src/schemas/{test-asset-typings.schema.json => source-static-asset-typings.schema.json} (100%) diff --git a/build-tests/heft-node-everything-esm-module-test/config/heft.json b/build-tests/heft-node-everything-esm-module-test/config/heft.json index 7893405e35d..e2e0619b020 100644 --- a/build-tests/heft-node-everything-esm-module-test/config/heft.json +++ b/build-tests/heft-node-everything-esm-module-test/config/heft.json @@ -12,10 +12,10 @@ "text-typings": { "taskPlugin": { "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", - "pluginName": "text-assets-plugin", + "pluginName": "source-assets-plugin", "options": { "configType": "file", - "configFileName": "text-assets.json" + "configFileName": "source-assets.json" } } }, diff --git a/build-tests/heft-node-everything-esm-module-test/config/text-assets.json b/build-tests/heft-node-everything-esm-module-test/config/source-assets.json similarity index 100% rename from build-tests/heft-node-everything-esm-module-test/config/text-assets.json rename to build-tests/heft-node-everything-esm-module-test/config/source-assets.json diff --git a/build-tests/heft-node-everything-test/config/heft.json b/build-tests/heft-node-everything-test/config/heft.json index 9ff31460373..ba8a4e8318b 100644 --- a/build-tests/heft-node-everything-test/config/heft.json +++ b/build-tests/heft-node-everything-test/config/heft.json @@ -19,7 +19,7 @@ "text-typings": { "taskPlugin": { "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", - "pluginName": "text-assets-plugin", + "pluginName": "source-assets-plugin", "options": { "configType": "inline", "config": { diff --git a/build-tests/heft-node-everything-test/config/text-assets.json b/build-tests/heft-node-everything-test/config/source-assets.json similarity index 100% rename from build-tests/heft-node-everything-test/config/text-assets.json rename to build-tests/heft-node-everything-test/config/source-assets.json diff --git a/build-tests/heft-rspack-everything-test/config/heft.json b/build-tests/heft-rspack-everything-test/config/heft.json index 757dacbf9be..9221aa19e99 100644 --- a/build-tests/heft-rspack-everything-test/config/heft.json +++ b/build-tests/heft-rspack-everything-test/config/heft.json @@ -13,7 +13,7 @@ "image-typings": { "taskPlugin": { "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", - "pluginName": "binary-assets-plugin", + "pluginName": "resource-assets-plugin", "options": { "configType": "inline", "config": { diff --git a/build-tests/heft-webpack4-everything-test/config/heft.json b/build-tests/heft-webpack4-everything-test/config/heft.json index 29914a13d13..e90bf3d3c74 100644 --- a/build-tests/heft-webpack4-everything-test/config/heft.json +++ b/build-tests/heft-webpack4-everything-test/config/heft.json @@ -13,10 +13,10 @@ "image-typings": { "taskPlugin": { "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", - "pluginName": "binary-assets-plugin", + "pluginName": "resource-assets-plugin", "options": { "configType": "file", - "configFileName": "binary-assets.json" + "configFileName": "resource-assets.json" } } }, diff --git a/build-tests/heft-webpack4-everything-test/config/binary-assets.json b/build-tests/heft-webpack4-everything-test/config/resource-assets.json similarity index 100% rename from build-tests/heft-webpack4-everything-test/config/binary-assets.json rename to build-tests/heft-webpack4-everything-test/config/resource-assets.json diff --git a/build-tests/heft-webpack5-everything-test/config/heft.json b/build-tests/heft-webpack5-everything-test/config/heft.json index dd557c18dd7..319963689d5 100644 --- a/build-tests/heft-webpack5-everything-test/config/heft.json +++ b/build-tests/heft-webpack5-everything-test/config/heft.json @@ -13,7 +13,7 @@ "image-typings": { "taskPlugin": { "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", - "pluginName": "binary-assets-plugin", + "pluginName": "resource-assets-plugin", "options": { "configType": "inline", "config": { diff --git a/heft-plugins/heft-static-asset-typings-plugin/README.md b/heft-plugins/heft-static-asset-typings-plugin/README.md index 6f814e59139..e73795a4223 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/README.md +++ b/heft-plugins/heft-static-asset-typings-plugin/README.md @@ -1,15 +1,21 @@ # @rushstack/heft-static-asset-typings-plugin This Heft plugin generates TypeScript `.d.ts` typings for static asset files, enabling type-safe -`import` statements for non-TypeScript resources. It provides two task plugins: +`import` statements for non-TypeScript files. It provides two task plugins: -- **`binary-assets-plugin`** — Generates `.d.ts` typings for binary files such as images (`.png`, - `.jpg`, `.svg`, etc.). Each matched file gets a typing that exports a default string value representing - the asset URL. +- **`resource-assets-plugin`** — Generates `.d.ts` typings for _resource_ files such as images (`.png`, + `.jpg`, `.svg`, etc.) and fonts. These are opaque binary blobs whose content is not meaningful to + JavaScript; the generated typing simply exports a default `string` representing the asset URL + (e.g. as resolved by a bundler's asset loader). -- **`text-assets-plugin`** — Generates `.d.ts` typings _and_ JavaScript module output for text-based - files (`.html`, `.txt`, `.md`, etc.). The generated JS modules export the file contents as a - default string, making text assets importable as ES modules. +- **`source-assets-plugin`** — Generates `.d.ts` typings _and_ JavaScript module output for _source_ + files (`.html`, `.css`, `.txt`, `.md`, etc.) whose textual content is consumed at runtime. The + generated JS modules read the file and re-export its content as a default `string`, making these + assets importable as ES modules. + +The terminology follows the [webpack convention](https://webpack.js.org/guides/asset-modules/) +where _resource_ assets are emitted as separate files referenced by URL, while _source_ assets are +inlined as strings. Both plugins support incremental and watch-mode builds. @@ -23,7 +29,7 @@ Both plugins support incremental and watch-mode builds. 2. Load the appropriate plugin(s) in your project's **config/heft.json**: - ### Binary assets (images, fonts, etc.) + ### Resource assets (images, fonts, etc.) **Inline configuration** — specify options directly in heft.json: @@ -36,7 +42,7 @@ Both plugins support incremental and watch-mode builds. "image-typings": { "taskPlugin": { "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", - "pluginName": "binary-assets-plugin", + "pluginName": "resource-assets-plugin", "options": { "configType": "inline", "config": { @@ -67,10 +73,10 @@ Both plugins support incremental and watch-mode builds. "image-typings": { "taskPlugin": { "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", - "pluginName": "binary-assets-plugin", + "pluginName": "resource-assets-plugin", "options": { "configType": "file", - "configFileName": "binary-assets.json" + "configFileName": "resource-assets.json" } } }, @@ -84,7 +90,7 @@ Both plugins support incremental and watch-mode builds. } ``` - And create a **config/binary-assets.json** file (which can be provided by a rig): + And create a **config/resource-assets.json** file (which can be provided by a rig): ```jsonc { @@ -93,7 +99,7 @@ Both plugins support incremental and watch-mode builds. } ``` - ### Text assets + ### Source assets **Inline configuration:** @@ -106,7 +112,7 @@ Both plugins support incremental and watch-mode builds. "text-typings": { "taskPlugin": { "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", - "pluginName": "text-assets-plugin", + "pluginName": "source-assets-plugin", "options": { "configType": "inline", "config": { @@ -139,10 +145,10 @@ Both plugins support incremental and watch-mode builds. "text-typings": { "taskPlugin": { "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", - "pluginName": "text-assets-plugin", + "pluginName": "source-assets-plugin", "options": { "configType": "file", - "configFileName": "text-assets.json" + "configFileName": "source-assets.json" } } }, @@ -156,7 +162,7 @@ Both plugins support incremental and watch-mode builds. } ``` - And create a **config/text-assets.json** file (which can be provided by a rig): + And create a **config/source-assets.json** file (which can be provided by a rig): ```jsonc { @@ -186,7 +192,7 @@ Both plugins support two configuration modes via the `configType` option: Provide configuration directly in heft.json under `options.config`: -#### `binary-assets-plugin` inline config +#### `resource-assets-plugin` inline config | Option | Type | Default | Description | | ------------------- | ---------- | ------------------------ | ----------------------------------------------- | @@ -194,7 +200,7 @@ Provide configuration directly in heft.json under `options.config`: | `generatedTsFolders`| `string[]` | `["temp/static-asset-ts"]` | Folders where generated `.d.ts` files are written. The first entry should be listed in `rootDirs` so TypeScript can resolve the asset imports during type-checking. Additional entries are typically your project's published typings folder(s). | | `sourceFolderPath` | `string` | `"src"` | Source folder to scan for asset files. | -#### `text-assets-plugin` inline config +#### `source-assets-plugin` inline config Includes all the above, plus: diff --git a/heft-plugins/heft-static-asset-typings-plugin/heft-plugin.json b/heft-plugins/heft-static-asset-typings-plugin/heft-plugin.json index b9d0644e0c4..73e8a17518f 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/heft-plugin.json +++ b/heft-plugins/heft-static-asset-typings-plugin/heft-plugin.json @@ -3,14 +3,14 @@ "taskPlugins": [ { - "pluginName": "binary-assets-plugin", - "entryPoint": "./lib-commonjs/BinaryAssetsPlugin", - "optionsSchema": "./lib-commonjs/schemas/binary-assets-options.schema.json" + "pluginName": "resource-assets-plugin", + "entryPoint": "./lib-commonjs/ResourceAssetsPlugin", + "optionsSchema": "./lib-commonjs/schemas/resource-assets-options.schema.json" }, { - "pluginName": "text-assets-plugin", - "entryPoint": "./lib-commonjs/TextAssetsPlugin", - "optionsSchema": "./lib-commonjs/schemas/text-assets-options.schema.json" + "pluginName": "source-assets-plugin", + "entryPoint": "./lib-commonjs/SourceAssetsPlugin", + "optionsSchema": "./lib-commonjs/schemas/source-assets-options.schema.json" } ] } diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/BinaryAssetsPlugin.ts b/heft-plugins/heft-static-asset-typings-plugin/src/ResourceAssetsPlugin.ts similarity index 85% rename from heft-plugins/heft-static-asset-typings-plugin/src/BinaryAssetsPlugin.ts rename to heft-plugins/heft-static-asset-typings-plugin/src/ResourceAssetsPlugin.ts index 0d19820a3bd..253f625fea6 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/BinaryAssetsPlugin.ts +++ b/heft-plugins/heft-static-asset-typings-plugin/src/ResourceAssetsPlugin.ts @@ -10,12 +10,12 @@ import { type IStaticAssetGeneratorOptions, type IStaticAssetTypingsGenerator } from './StaticAssetTypingsGenerator'; -import type { IAssetPluginOptions, IBinaryStaticAssetTypingsConfigurationJson } from './types'; +import type { IAssetPluginOptions, IResourceStaticAssetTypingsConfigurationJson } from './types'; const PLUGIN_NAME: 'static-asset-typings-plugin' = 'static-asset-typings-plugin'; -export default class BinaryAssetsPlugin - implements IHeftTaskPlugin> +export default class ResourceAssetsPlugin + implements IHeftTaskPlugin> { /** * Generate typings for text files before TypeScript compilation. @@ -23,7 +23,7 @@ export default class BinaryAssetsPlugin public apply( taskSession: IHeftTaskSession, heftConfiguration: HeftConfiguration, - options: IAssetPluginOptions + options: IAssetPluginOptions ): void { const { slashNormalizedBuildFolderPath, rigConfig } = heftConfiguration; const staticAssetGeneratorOptions: IStaticAssetGeneratorOptions = { @@ -33,7 +33,7 @@ export default class BinaryAssetsPlugin slashNormalizedBuildFolderPath, rigConfig, options, - 'binary' + 'resource' ); }, slashNormalizedBuildFolderPath, diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/TextAssetsPlugin.ts b/heft-plugins/heft-static-asset-typings-plugin/src/SourceAssetsPlugin.ts similarity index 91% rename from heft-plugins/heft-static-asset-typings-plugin/src/TextAssetsPlugin.ts rename to heft-plugins/heft-static-asset-typings-plugin/src/SourceAssetsPlugin.ts index 7a39a9ecc84..edc1b09cf2a 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/TextAssetsPlugin.ts +++ b/heft-plugins/heft-static-asset-typings-plugin/src/SourceAssetsPlugin.ts @@ -13,9 +13,9 @@ import { type IStaticAssetGeneratorOptions, type IStaticAssetTypingsGenerator } from './StaticAssetTypingsGenerator'; -import type { IAssetPluginOptions, ITextStaticAssetTypingsConfigurationJson } from './types'; +import type { IAssetPluginOptions, ISourceStaticAssetTypingsConfigurationJson } from './types'; -const PLUGIN_NAME: 'text-assets-plugin' = 'text-assets-plugin'; +const PLUGIN_NAME: 'source-assets-plugin' = 'source-assets-plugin'; // Pre-allocated preamble/postamble buffers to avoid repeated allocations. // Used with FileSystem.writeBuffersToFileAsync (writev) for efficient output. @@ -26,8 +26,8 @@ const CJS_POSTAMBLE: Buffer = Buffer.from(';\nexports.default = content;\n'); const ESM_PREAMBLE: Buffer = Buffer.from('const content = '); const ESM_POSTAMBLE: Buffer = Buffer.from(';\nexport default content;\n'); -export default class TextAssetsPlugin - implements IHeftTaskPlugin> +export default class SourceAssetsPlugin + implements IHeftTaskPlugin> { /** * Generate typings for text files before TypeScript compilation. @@ -35,20 +35,20 @@ export default class TextAssetsPlugin public apply( taskSession: IHeftTaskSession, heftConfiguration: HeftConfiguration, - pluginOptions: IAssetPluginOptions + pluginOptions: IAssetPluginOptions ): void { let generatorPromise: Promise | undefined; async function initializeGeneratorAsync(): Promise { const { slashNormalizedBuildFolderPath, rigConfig } = heftConfiguration; - const options: ITextStaticAssetTypingsConfigurationJson | undefined = + const options: ISourceStaticAssetTypingsConfigurationJson | undefined = await tryGetConfigFromPluginOptionsAsync( taskSession.logger.terminal, slashNormalizedBuildFolderPath, rigConfig, pluginOptions, - 'text' + 'source' ); if (options) { diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts b/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts index b36a869fb75..41fc438f00b 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts +++ b/heft-plugins/heft-static-asset-typings-plugin/src/StaticAssetTypingsGenerator.ts @@ -16,8 +16,8 @@ import type { ITerminal } from '@rushstack/terminal'; import type { IAssetPluginOptions, - IBinaryStaticAssetTypingsConfigurationJson, - ITextStaticAssetTypingsConfigurationJson, + IResourceStaticAssetTypingsConfigurationJson, + ISourceStaticAssetTypingsConfigurationJson, StaticAssetConfigurationFileLoader } from './types'; @@ -89,26 +89,26 @@ export async function tryGetConfigFromPluginOptionsAsync( terminal: ITerminal, buildFolder: string, rigConfig: HeftConfiguration['rigConfig'], - options: IAssetPluginOptions, - type: 'binary' -): Promise; + options: IAssetPluginOptions, + type: 'resource' +): Promise; export async function tryGetConfigFromPluginOptionsAsync( terminal: ITerminal, buildFolder: string, rigConfig: HeftConfiguration['rigConfig'], - options: IAssetPluginOptions, - type: 'text' -): Promise; + options: IAssetPluginOptions, + type: 'source' +): Promise; export async function tryGetConfigFromPluginOptionsAsync( terminal: ITerminal, buildFolder: string, rigConfig: HeftConfiguration['rigConfig'], options: IAssetPluginOptions< - IBinaryStaticAssetTypingsConfigurationJson | ITextStaticAssetTypingsConfigurationJson + IResourceStaticAssetTypingsConfigurationJson | ISourceStaticAssetTypingsConfigurationJson >, type: import('./getConfigFromConfigFileAsync').FileLoaderType ): Promise< - IBinaryStaticAssetTypingsConfigurationJson | ITextStaticAssetTypingsConfigurationJson | undefined + IResourceStaticAssetTypingsConfigurationJson | ISourceStaticAssetTypingsConfigurationJson | undefined > { if (options?.configType === 'inline') { return options.config; @@ -135,7 +135,7 @@ export async function createTypingsGeneratorAsync( const { terminal } = taskSession.logger; - const configuration: IBinaryStaticAssetTypingsConfigurationJson | undefined = + const configuration: IResourceStaticAssetTypingsConfigurationJson | undefined = await tryGetConfigAsync(terminal); if (!configuration) { diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/getConfigFromConfigFileAsync.ts b/heft-plugins/heft-static-asset-typings-plugin/src/getConfigFromConfigFileAsync.ts index b28dd758f40..c25341890c4 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/getConfigFromConfigFileAsync.ts +++ b/heft-plugins/heft-static-asset-typings-plugin/src/getConfigFromConfigFileAsync.ts @@ -6,31 +6,31 @@ import { InheritanceType, ProjectConfigurationFile } from '@rushstack/heft-confi import type { ITerminal } from '@rushstack/terminal'; import type { - IBinaryStaticAssetTypingsConfigurationJson, - ITextStaticAssetTypingsConfigurationJson + IResourceStaticAssetTypingsConfigurationJson, + ISourceStaticAssetTypingsConfigurationJson } from './types'; -import binaryStaticAssetSchema from './schemas/binary-static-asset-typings.schema.json'; -import textStaticAssetSchema from './schemas/test-asset-typings.schema.json'; +import resourceStaticAssetSchema from './schemas/resource-static-asset-typings.schema.json'; +import sourceStaticAssetSchema from './schemas/source-static-asset-typings.schema.json'; const configurationFileLoaderByFileName: Map< string, ProjectConfigurationFile< - IBinaryStaticAssetTypingsConfigurationJson | ITextStaticAssetTypingsConfigurationJson + IResourceStaticAssetTypingsConfigurationJson | ISourceStaticAssetTypingsConfigurationJson > > = new Map(); -export type FileLoaderType = 'binary' | 'text'; +export type FileLoaderType = 'resource' | 'source'; function createConfigurationFileLoader( configFileName: string, fileLoaderType: FileLoaderType ): ProjectConfigurationFile< - IBinaryStaticAssetTypingsConfigurationJson | ITextStaticAssetTypingsConfigurationJson + IResourceStaticAssetTypingsConfigurationJson | ISourceStaticAssetTypingsConfigurationJson > { return new ProjectConfigurationFile< - IBinaryStaticAssetTypingsConfigurationJson | ITextStaticAssetTypingsConfigurationJson + IResourceStaticAssetTypingsConfigurationJson | ISourceStaticAssetTypingsConfigurationJson >({ - jsonSchemaObject: fileLoaderType === 'binary' ? binaryStaticAssetSchema : textStaticAssetSchema, + jsonSchemaObject: fileLoaderType === 'resource' ? resourceStaticAssetSchema : sourceStaticAssetSchema, projectRelativeFilePath: `config/${configFileName}`, propertyInheritance: { fileExtensions: { @@ -47,11 +47,11 @@ export function getConfigFromConfigFileAsync( slashNormalizedBuildFolderPath: string, rigConfig: HeftConfiguration['rigConfig'] ): Promise< - IBinaryStaticAssetTypingsConfigurationJson | ITextStaticAssetTypingsConfigurationJson | undefined + IResourceStaticAssetTypingsConfigurationJson | ISourceStaticAssetTypingsConfigurationJson | undefined > { let configurationFileLoader: | ProjectConfigurationFile< - IBinaryStaticAssetTypingsConfigurationJson | ITextStaticAssetTypingsConfigurationJson + IResourceStaticAssetTypingsConfigurationJson | ISourceStaticAssetTypingsConfigurationJson > | undefined = configurationFileLoaderByFileName.get(configFileName); if (!configurationFileLoader) { diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/schemas/binary-assets-options.schema.json b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/resource-assets-options.schema.json similarity index 100% rename from heft-plugins/heft-static-asset-typings-plugin/src/schemas/binary-assets-options.schema.json rename to heft-plugins/heft-static-asset-typings-plugin/src/schemas/resource-assets-options.schema.json diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/schemas/binary-static-asset-typings.schema.json b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/resource-static-asset-typings.schema.json similarity index 100% rename from heft-plugins/heft-static-asset-typings-plugin/src/schemas/binary-static-asset-typings.schema.json rename to heft-plugins/heft-static-asset-typings-plugin/src/schemas/resource-static-asset-typings.schema.json diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/schemas/text-assets-options.schema.json b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/source-assets-options.schema.json similarity index 100% rename from heft-plugins/heft-static-asset-typings-plugin/src/schemas/text-assets-options.schema.json rename to heft-plugins/heft-static-asset-typings-plugin/src/schemas/source-assets-options.schema.json diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/schemas/test-asset-typings.schema.json b/heft-plugins/heft-static-asset-typings-plugin/src/schemas/source-static-asset-typings.schema.json similarity index 100% rename from heft-plugins/heft-static-asset-typings-plugin/src/schemas/test-asset-typings.schema.json rename to heft-plugins/heft-static-asset-typings-plugin/src/schemas/source-static-asset-typings.schema.json diff --git a/heft-plugins/heft-static-asset-typings-plugin/src/types.ts b/heft-plugins/heft-static-asset-typings-plugin/src/types.ts index 32db8776635..c5c9256ce02 100644 --- a/heft-plugins/heft-static-asset-typings-plugin/src/types.ts +++ b/heft-plugins/heft-static-asset-typings-plugin/src/types.ts @@ -4,7 +4,7 @@ import type { ITerminal } from '@rushstack/terminal'; export interface IAssetsInlineConfigPluginOptionsBase< - TConfig extends IBinaryStaticAssetTypingsConfigurationJson + TConfig extends IResourceStaticAssetTypingsConfigurationJson > { configType: 'inline'; /** @@ -21,21 +21,22 @@ export interface IAssetsFileConfigPluginOptions { configFileName: string; } -export type IAssetPluginOptions = +export type IAssetPluginOptions = | IAssetsInlineConfigPluginOptionsBase | IAssetsFileConfigPluginOptions; -export interface IBinaryStaticAssetTypingsConfigurationJson { +export interface IResourceStaticAssetTypingsConfigurationJson { fileExtensions: string[]; generatedTsFolders?: string[]; sourceFolderPath?: string; } -export interface ITextStaticAssetTypingsConfigurationJson extends IBinaryStaticAssetTypingsConfigurationJson { +export interface ISourceStaticAssetTypingsConfigurationJson + extends IResourceStaticAssetTypingsConfigurationJson { cjsOutputFolders: string[]; esmOutputFolders?: string[]; } export type StaticAssetConfigurationFileLoader = ( terminal: ITerminal -) => Promise; +) => Promise; diff --git a/rigs/heft-web-rig/profiles/app/config/heft.json b/rigs/heft-web-rig/profiles/app/config/heft.json index 09ac1eabff2..92e332665a0 100644 --- a/rigs/heft-web-rig/profiles/app/config/heft.json +++ b/rigs/heft-web-rig/profiles/app/config/heft.json @@ -51,7 +51,7 @@ "taskDependencies": ["set-browserslist-ignore-old-data-env-var"], "taskPlugin": { "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", - "pluginName": "binary-assets-plugin", + "pluginName": "resource-assets-plugin", "options": { "configType": "inline", "config": { diff --git a/rigs/heft-web-rig/profiles/library/config/heft.json b/rigs/heft-web-rig/profiles/library/config/heft.json index 09ac1eabff2..92e332665a0 100644 --- a/rigs/heft-web-rig/profiles/library/config/heft.json +++ b/rigs/heft-web-rig/profiles/library/config/heft.json @@ -51,7 +51,7 @@ "taskDependencies": ["set-browserslist-ignore-old-data-env-var"], "taskPlugin": { "pluginPackage": "@rushstack/heft-static-asset-typings-plugin", - "pluginName": "binary-assets-plugin", + "pluginName": "resource-assets-plugin", "options": { "configType": "inline", "config": {