Remove Code Blocks from TypeScript and HTML Files Using Custom Angular Builder In Angular 16

Discussão em 'Angular' iniciado por Blue Clock, Fevereiro 13, 2025.

  1. Blue Clock

    Blue Clock Guest

    I'm developing a custom Angular builder to remove specific code blocks from both TypeScript and HTML files during the build process. While the TypeScript processing works correctly, I'm unable to get the HTML processing working.

    What I Want to Achieve


    1. Remove code blocks from TypeScript files that are wrapped in: //#REMOVE_BLOCK_START // some code here //#REMOVE_BLOCK_END


    2. Remove code blocks from HTML files that are wrapped in: <!-- #REMOVE_BLOCK_START --> <!-- some html here --> <!-- #REMOVE_BLOCK_END -->

    Project Structure

    my-project/
    ├── client/ # Angular application
    │ ├── angular.json
    │ └── src/
    └── custom-builder/ # Custom builder implementation
    ├── src/
    │ ├── custom-builder.ts
    │ ├── HtmlContentModifierPlugin.ts
    │ └── remove_module.ts
    ├── builders.json
    └── package.json


    Code Implementation

    1. Custom Builder (custom-builder.ts):

    import * as path from 'path';
    import * as webpack from 'webpack';
    import { BuilderContext, createBuilder } from '@angular-devkit/architect';
    import { executeBrowserBuilder } from '@angular-devkit/build-angular';
    import { HtmlContentModifierPlugin } from './HtmlContentModifierPlugin';

    function transform(input: webpack.Configuration): webpack.Configuration {
    if (!input.module?.rules) return input;

    // Add rule for TypeScript and HTML files
    // __ditname refers as 'my-project/custom-builder/dist
    // (since the remove_module.ts will be converted in remove_moduel.js we are passing it in loader)
    const newRule = {
    test: /\.(ts|html)$/,
    exclude: /node_modules/,
    use: [
    {
    loader: path.resolve(__dirname, 'remove_module.js'),
    options: {}
    }
    ]
    };

    input.module.rules.unshift(newRule);

    // Add HTML plugin
    if (!input.plugins) {
    input.plugins = [];
    }
    input.plugins.push(new HtmlContentModifierPlugin());

    return input;
    }

    function executeBuilder(options: any, context: BuilderContext) {
    return executeBrowserBuilder(options, context, {
    webpackConfiguration: transform
    });
    }

    export default createBuilder(executeBuilder);

    1. HTML Plugin (HtmlContentModifierPlugin.ts):

    import { Compiler, sources } from 'webpack';
    import { Compilation } from 'webpack';

    export class HtmlContentModifierPlugin {
    apply(compiler: Compiler): void {
    compiler.hooks.emit.tapAsync(
    'HtmlContentModifierPlugin',
    (compilation: Compilation, callback) => {
    // Process all compiled assets
    Object.keys(compilation.assets).forEach((filename) => {
    if (filename.endsWith('.html')) {
    console.log('Processing HTML file:', filename);
    const asset = compilation.assets[filename];
    const content = asset.source().toString();

    // Remove assessment blocks
    const modifiedContent = content.replace(
    /<!--\s*#REMOVE_BLOCK_START\s*-->[\s\S]*?<!--\s*#REMOVE_BLOCK_END\s*-->/g,
    ''
    );

    compilation.assets[filename] = new sources.RawSource(modifiedContent);
    }
    });
    callback();
    }
    );
    }
    }

    1. remove_module.ts:

    import { LoaderDefinitionFunction } from 'webpack';

    const removeAssessmentLoader: LoaderDefinitionFunction = function (source) {
    const filename = this.resourcePath;
    console.log("Code is running with loader for:", filename);

    // Replace all occurrences of "originalText" with an empty string

    if (filename.endsWith('.ts')) {
    return source.replace(/\/\/#REMOVE_BLOCK_START[\s\S]*?\/\/#REMOVE_BLOCK_END/g, '');
    }

    // Remove HTML code between <!-- #REMOVE_BLOCK_START --> and <!-- #REMOVE_BLOCK_END -->
    else if (filename.endsWith('.html')) {
    console.log("HTML CHANGE");
    return source.replace(/<!--\s*#REMOVE_BLOCK_START\s*-->[\s\S]*?<!--\s*#REMOVE_BLOCK_END\s*-->/g, '');
    }

    };

    export default removeAssessmentLoader;

    1. custom-builder/package.json:

    {
    "name": "custom-builder",
    "version": "0.0.1",
    "builders": "./builders.json",
    "scripts": {
    "build": "tsc"
    },
    "devDependencies": {
    "@angular-devkit/architect": "0.1602.16",
    "@angular-devkit/build-angular": "^16.2.16",
    "@angular-devkit/core": "^16.2.16",
    "@types/karma": "^6.3.9",
    "@types/node": "~18.11.6",
    "karma": "^6.4.1",
    "rxjs": "~7.5.0",
    "typescript": "^5.1.6"
    },
    "dependencies": {
    "@angular-devkit/schematics": "^16.2.16",
    "@angular/cli": "^16.2.16"
    }
    }

    1. angular.json Configuration.

    {
    "projects": {
    "client": {
    "architect": {
    "custom-target": {
    "builder": "@angular-builders/custom-webpack:browser",
    "options": {
    "outputPath": "dist/client",
    "index": "src/index.html",
    "main": "src/main.ts",
    "polyfills": "src/polyfills.ts",
    "tsConfig": "src/tsconfig.app.json"
    },
    "configurations": {
    "production": {
    "optimization": true,
    "outputHashing": "none",
    "sourceMap": false,
    "namedChunks": false,
    "extractLicenses": true,
    "vendorChunk": false,
    "buildOptimizer": true
    }
    }
    }
    }
    }
    }
    }


    What's Working The TypeScript processing works perfectly. All code blocks between //#REMOVE_BLOCK_START and //#REMOVE_BLOCK_END are successfully removed during build.

    What is Not Working The HTML processing is not working. The HTML files are not being processed, and the code blocks between <!-- #REMOVE_BLOCK_START --> and <!-- #REMOVE_BLOCK_END --> remain in the output

    Questions

    • Why isn't the HTML plugin processing the HTML files?
    • Is there a better approach to process HTML files during the Angular build process? ( I have heard about AngularWebpackPlugin but i'm not sure if it has anything like transform to perform function at compilation time)

    Continue reading...

Compartilhe esta Página