From afb928efc36e5a0b7a71e2599b7c9ff57b6aac6a Mon Sep 17 00:00:00 2001 From: "Houston (Jeb's AI)" Date: Mon, 27 Apr 2026 08:08:45 +0000 Subject: [PATCH] docs(custom-esbuild): document target.configuration in plugin and indexHtmlTransformer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add target.configuration to the factory plugin example in README (alongside the existing target.project usage), with inline comments explaining each field - Update indexHtmlTransformer examples (both JS and TS) to include the target param with comments showing target.configuration and target.project - Add integration test 'target-options-in-plugin-and-transformer' that does a real ng build with a factory plugin and indexHtmlTransformer both reading target.configuration, then verifies the value appears in the JS bundle and index.html meta tag — confirming the feature works end-to-end - Add esbuild/define-configuration-plugin.js and esbuild/configuration-transformer.js to sanity-esbuild-app as the test fixtures Closes #1710 Closes #1690 --- .../sanity-esbuild-app/angular.json | 10 ++++++++- .../esbuild/configuration-transformer.js | 10 +++++++++ .../esbuild/define-configuration-plugin.js | 21 +++++++++++++++++++ .../sanity-esbuild-app/package.json | 5 +++-- .../src/app/app.component.html | 1 + .../src/app/app.component.ts | 3 +++ packages/custom-esbuild/README.md | 13 ++++++++++-- packages/custom-esbuild/tests/integration.js | 10 +++++++++ 8 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 examples/custom-esbuild/sanity-esbuild-app/esbuild/configuration-transformer.js create mode 100644 examples/custom-esbuild/sanity-esbuild-app/esbuild/define-configuration-plugin.js diff --git a/examples/custom-esbuild/sanity-esbuild-app/angular.json b/examples/custom-esbuild/sanity-esbuild-app/angular.json index bf46ee4396..068af97671 100644 --- a/examples/custom-esbuild/sanity-esbuild-app/angular.json +++ b/examples/custom-esbuild/sanity-esbuild-app/angular.json @@ -80,6 +80,14 @@ } } ] + }, + "target-options-test": { + "plugins": [ + "esbuild/define-configuration-plugin.js" + ], + "indexHtmlTransformer": "esbuild/configuration-transformer.js", + "optimization": false, + "outputHashing": "none" } }, "defaultConfiguration": "production" @@ -206,4 +214,4 @@ "typeSeparator": "." } } -} +} \ No newline at end of file diff --git a/examples/custom-esbuild/sanity-esbuild-app/esbuild/configuration-transformer.js b/examples/custom-esbuild/sanity-esbuild-app/esbuild/configuration-transformer.js new file mode 100644 index 0000000000..63bb308295 --- /dev/null +++ b/examples/custom-esbuild/sanity-esbuild-app/esbuild/configuration-transformer.js @@ -0,0 +1,10 @@ +/** + * Index HTML transformer for issues #1690 / #1710 test. + * Signature: (indexHtml: string, target: Target) => string + * Injects a tag with the configuration name so we can verify it in the output file. + */ +module.exports = function configurationTransformer(indexHtml, target) { + const configuration = target.configuration ?? 'default'; + const metaTag = ``; + return indexHtml.replace('', ` ${metaTag}\n`); +}; diff --git a/examples/custom-esbuild/sanity-esbuild-app/esbuild/define-configuration-plugin.js b/examples/custom-esbuild/sanity-esbuild-app/esbuild/define-configuration-plugin.js new file mode 100644 index 0000000000..8a5bb7e45a --- /dev/null +++ b/examples/custom-esbuild/sanity-esbuild-app/esbuild/define-configuration-plugin.js @@ -0,0 +1,21 @@ +/** + * Factory plugin (Pattern 1: string path in angular.json). + * Receives (builderOptions, target) from the builder. + * Injects `target.configuration` as a global define constant `buildConfiguration`. + * + * This is the test for issues #1710 and #1690: + * verifying that target.configuration is accessible inside a factory plugin. + */ +function defineConfigurationPlugin(builderOptions, target) { + const configuration = target.configuration ?? 'default'; + return { + name: 'define-configuration', + setup(build) { + const options = build.initialOptions; + options.define = options.define || {}; + options.define.buildConfiguration = JSON.stringify(configuration); + }, + }; +} + +module.exports = defineConfigurationPlugin; diff --git a/examples/custom-esbuild/sanity-esbuild-app/package.json b/examples/custom-esbuild/sanity-esbuild-app/package.json index fd76748c4a..ff4c824081 100644 --- a/examples/custom-esbuild/sanity-esbuild-app/package.json +++ b/examples/custom-esbuild/sanity-esbuild-app/package.json @@ -8,7 +8,8 @@ "lint": "ng lint", "e2e": "ng e2e", "cypress:open": "cypress open", - "cypress:run": "cypress run" + "cypress:run": "cypress run", + "build-target-options": "ng build --configuration target-options-test && node -e \"const fs = require('fs');const dist = 'dist/sanity-esbuild-app/browser';const indexHtml = fs.readFileSync(dist + '/index.html', 'utf8');const jsFiles = fs.readdirSync(dist).filter(f => f.endsWith('.js'));const mainJs = jsFiles.map(f => fs.readFileSync(dist + '/' + f, 'utf8')).join('');if (!indexHtml.includes('content=\\\"target-options-test\\\"')) { console.error('FAIL: indexHtmlTransformer did not inject configuration into index.html'); process.exit(1); }if (!mainJs.includes('target-options-test')) { console.error('FAIL: plugin did not inject buildConfiguration into JS bundle'); process.exit(1); }console.log('PASS: target.configuration is accessible in both plugin and indexHtmlTransformer');\"" }, "private": true, "dependencies": { @@ -42,4 +43,4 @@ "typescript-eslint": "8.59.0", "vitest": "4.1.5" } -} +} \ No newline at end of file diff --git a/examples/custom-esbuild/sanity-esbuild-app/src/app/app.component.html b/examples/custom-esbuild/sanity-esbuild-app/src/app/app.component.html index 7034c8f302..7e7a14d708 100644 --- a/examples/custom-esbuild/sanity-esbuild-app/src/app/app.component.html +++ b/examples/custom-esbuild/sanity-esbuild-app/src/app/app.component.html @@ -1,3 +1,4 @@

{{ title }}

{{ subtitle }}

{{ titleByOption }}

+

{{ buildConfiguration }}

diff --git a/examples/custom-esbuild/sanity-esbuild-app/src/app/app.component.ts b/examples/custom-esbuild/sanity-esbuild-app/src/app/app.component.ts index b878467344..ca0d751028 100644 --- a/examples/custom-esbuild/sanity-esbuild-app/src/app/app.component.ts +++ b/examples/custom-esbuild/sanity-esbuild-app/src/app/app.component.ts @@ -3,6 +3,7 @@ import { Component } from '@angular/core'; declare const title: string; declare const subtitle: string; declare const titleByOption: string; +declare const buildConfiguration: string; @Component({ selector: 'app-root', @@ -14,10 +15,12 @@ export class AppComponent { title: string; subtitle: string; titleByOption: string; + buildConfiguration: string; constructor() { this.title = typeof title !== 'undefined' ? title : 'sanity-esbuild-app'; this.subtitle = typeof subtitle !== 'undefined' ? subtitle : 'sanity-esbuild-app subtitle'; this.titleByOption = typeof titleByOption !== 'undefined' ? titleByOption : 'sanity-esbuild-app optionTitle'; + this.buildConfiguration = typeof buildConfiguration !== 'undefined' ? buildConfiguration : 'no-config'; } } diff --git a/packages/custom-esbuild/README.md b/packages/custom-esbuild/README.md index 7fcaf14728..931877908c 100644 --- a/packages/custom-esbuild/README.md +++ b/packages/custom-esbuild/README.md @@ -202,7 +202,10 @@ export default (builderOptions: ApplicationBuilderOptions, target: Target): Plug name: 'define-text', setup(build: PluginBuild) { const options = build.initialOptions; + // target.project is the Angular project name (e.g. "my-app") options.define.currentProject = JSON.stringify(target.project); + // target.configuration is the active build configuration (e.g. "production", "staging") + options.define.currentConfiguration = JSON.stringify(target.configuration ?? 'default'); }, }; }; @@ -296,7 +299,9 @@ It is useful when you want to transform your `index.html` according to the build `index-html-transformer.js`: ```js -module.exports = indexHtml => { +module.exports = (indexHtml, target) => { + // target.configuration is the active build configuration (e.g. "production", "staging") + // target.project is the Angular project name const i = indexHtml.indexOf(''); const content = `

Dynamically inserted content

`; return `${indexHtml.slice(0, i)} @@ -308,7 +313,11 @@ module.exports = indexHtml => { Alternatively, using TypeScript: ```ts -export default (indexHtml: string) => { +import type { Target } from '@angular-devkit/architect'; + +export default (indexHtml: string, target: Target) => { + // target.configuration is the active build configuration (e.g. "production", "staging") + // target.project is the Angular project name const i = indexHtml.indexOf(''); const content = `

Dynamically inserted content

`; return `${indexHtml.slice(0, i)} diff --git a/packages/custom-esbuild/tests/integration.js b/packages/custom-esbuild/tests/integration.js index 4932b6ed73..ea0acdbce3 100644 --- a/packages/custom-esbuild/tests/integration.js +++ b/packages/custom-esbuild/tests/integration.js @@ -88,4 +88,14 @@ module.exports = [ app: 'examples/custom-esbuild/sanity-esbuild-app-esm', command: 'yarn build-ts -c tsEsm', }, + + // Target options accessibility (#1690, #1710) + { + id: 'target-options-in-plugin-and-transformer', + name: 'custom-esbuild: target.configuration accessible in plugin and indexHtmlTransformer', + purpose: + 'Factory plugin and indexHtmlTransformer both receive target.configuration at runtime', + app: 'examples/custom-esbuild/sanity-esbuild-app', + command: 'yarn build-target-options', + }, ];