angularjs-to-angular

Migrate an AngularJS codebase to an Angular codebase

Example

constants:
  angular_directory: 'angular'
  src_directory: '{{ angular_directory }}/src'
  app_directory: '{{ src_directory }}/app'
  assets_directory: '{{ src_directory }}/assets'
  entry_file_path: '{{ src_directory }}/main.ts'
  entry_file_path_template: '{{ src_directory }}/index.html'
  constants_directory: '{{ app_directory }}/constants'
  components_directory: '{{ app_directory }}/components'
  services_directory: '{{ app_directory }}/services'
  directives_directory: '{{ app_directory }}/directives'
  pipes_directory: '{{ app_directory }}/pipes'
  modules_directory: '{{ app_directory }}/modules'
  context_depth: 3
  dependencies:
    - 'rxjs'
    - '@angular/router'
    - '@angular/forms'
    - '@angular/common'
    - '@angular/platform-browser'
    - '@angular/platform-browser-dynamic'
  dev_dependencies:
    - '@angular/cli'

  library_mappings: |
    - All `$http` and `$httpProvider` usages should be converted/replaced with `HttpClient` from `@angular/common/http`. IMPORTANT: Always use `provideHttpClient(withInterceptorsFromDi())` instead of `HttpClientModule`.
    - All `$location` usages should be converted/replaced with Angular 16's `Router`.
    - All `$scope` usages should be converted/replaced with component properties and Angular 16's data binding.
    - All `angular-aria` usages should be handled with Angular 16's built-in accessibility features.
    - All `angular-loader` usages should be converted/replaced with Angular 16's lazy loading modules.
    - All `angular-messages` usages should be converted/replaced with Angular 16's form validation features.
    - All `angular-resource` usages should be converted/replaced with `HttpClient`.
    - All `angular-route` usages should be converted/replaced with `@angular/router`.
    - All `angular-touch` usages should be converted/replaced with Angular 16's event handling.
    - All `$timeout` usages should be converted/replaced with the `setTimeout` function.
    - All `$interval` usages should be converted/replaced with the `setInterval` function.
    - All `angular.noop` usages should be converted/replaced with the rxjs `noop` function (don't forget to import it from `rxjs`).

  transformation_notes: |
    ## Codebase Transformation Notes
    > IMPORTANT: YOU MUST ALWAYS OUTPUT CODE (Typescript, HTML, or SCSS) THAT IS VALID AND COMPATIBLE WITH ANGULAR 16. NEVER OUTPUT ANGULARJS CODE.

    ### Generation Preferences
    When generating code, follow these guidelines:
    - Only include information from the portion you were told to convert.
    - Do not create new configs, components, or example usages.
    - Remove AngularJS-specific elements that cannot be converted to Angular 16.
    - Leave empty functions as they are.
    - If the current file path was provided above, you should ensure that the import paths are relative to this path.
    - At the top of the converted file, you should include a comment with the original file path. Something like `// Converted from <original-file-path>`.
    - The resulting code should be DRY and only declare one component per file.
    - The resulting code should be written in TypeScript.
    - You should always define and export classes or functions as appropriate.
    - Always include comments from the original code that are still relevant after conversion.
    - ALWAYS use ES6+ syntax for typescript code. That means use the spread operator, arrow functions, etc where possible.
    - Prefer to use async/await over promises wherever possible. For example, `await somePromise` is better than `somePromise.then(...)`, making the surrounding function async as needed. If the function is a built-in angular method, you should not make it async as that is not an allowed pattern.
    - When setting variables to undefined values, use `null` instead.
    - Prefer Angular 16's dependency injection and service mechanisms.
    - Do not hallucinate!

    ### AngularJS to Angular 16 Conversion Guidelines
    - AngularJS `ng-` and `data-ng-` directives and attributes should be converted to the Angular 16-equivalent logic. Do not leave these in the template.
    - Special built-in AngularJS services and factories, typically defined as `$<service-name>`, should be converted to Angular 16-equivalent logic.
    - AngularJS injectors are no longer needed in Angular 16 (since Angular uses its own dependency injection mechanism).
    - Local variables accessed through `$ctrl` or `$scope` in AngularJS should be converted to direct references in Angular 16. In templates, replace `$ctrl.myVar` or `$scope.myVar` with just `myVar`. In TypeScript files, replace references to `$ctrl` or `$scope` properties with direct class property/method access. 
    - Whenever you see some variable (e.g. `myVar`) passed in with the AngularJS one-way binding shorthand (e.g. `data-users-in-org="::$ctrl.myVar"`), you should convert it to `myVar` in the template (e.g. `usersInOrg="myVar"`).
    - All events that start with `ng-` or `data-ng-` should be converted/replaced with their Angular 16-equivalent. For example, both `ng-click` and `data-ng-click` should be converted/replaced with `(click)` event binding, both `ng-change` and `data-ng-change` should be converted/replaced with `(change)` event binding, etc.
    - All attributes that start with `data-ng-` should be converted/replaced with their Angular 16-equivalent. For example, both `ng-class` and `data-ng-class` should be converted/replaced with `[ngClass]` directive, both `ng-href` and `data-ng-href` should be converted/replaced with `[href]` property binding, etc.
    - All `ng-model` or `data-ng-model` usages should be converted/replaced with `[(ngModel)]` for two-way data binding.
    - All `ng-disabled` or `data-ng-disabled` usages should be converted/replaced with `[disabled]` property binding.
    - All `ng-style` or `data-ng-style` usages should be converted/replaced with `[ngStyle]` directive.
    - All `ng-repeat` or `data-ng-repeat` usages should be converted/replaced with `*ngFor` directive. All *ngFor directives must also include `trackby` with a trackby function.
    - All `ng-if` or `data-ng-if` usages should be converted/replaced with `*ngIf` directive.
    - All `ng-show` or `data-ng-show` and `ng-hide` or `data-ng-hide` usages should be converted/replaced with `*ngIf` or `[hidden]` property binding.
    - All `ng-bind-html` or `data-ng-bind-html` usages should be converted/replaced with `[innerHTML]` property binding.
    - All `ng-bind` or `data-ng-bind` usages should be converted/replaced with `{{ }}` interpolation.
    - All `ng-class` or `data-ng-class` usages should be converted/replaced with `[ngClass]` directive.
    - All `ng-transclude` or `data-ng-transclude` usages should be converted/replaced with Angular 16's `<ng-content>`. For named slots, use `<ng-content select="[slot-name]"></ng-content>`.
    - All `ng-include` or `data-ng-include` usages should be converted/replaced with the corresponding Angular 16 component. Use the component's selector in place of the ng-include reference (e.g., `<my-component>`), assuming the component has already been converted according to the “New Files” criteria below.

    ### New Files
    When defining new files based on unconverted files, assume the following naming strategies (useful for imports):
    - AngularJS constants and values → TypeScript constants exported as the name of the constant in `{{ constants_directory }}/<constant-name>.ts`.
    - AngularJS services, factories, and providers → Angular services in `{{ services_directory }}/<current-file-name>.service.ts`. Each service should be decorated with `@Injectable()` and properly provided in modules.
    - AngularJS pure directives (directives without templates) → Angular directives in `{{ directives_directory }}/<current-file-name>.directive.ts`.
    - AngularJS filters → Angular pipes in `{{ pipes_directory }}/<current-file-name>.pipe.ts`.
    - AngularJS controllers, components, and directives with templates → Angular components in `{{ components_directory }}/<current-file-name>.component.ts` (note: we keep the components structure flat with no folder nesting.) This will export a single default class.
    - AngularJS template HTML files → Angular template HTML in `{{ components_directory }}/<current-file-name>.html`.
    - AngularJS config and routes → Angular modules and routing modules in `{{ modules_directory }}/<current-file-name>.module.ts`.

    ### Libraries
    The following libraries are already installed in the project and available for you to use (in addition to the default Angular 16 libraries): {{ dependencies }}

    #### Mapping AngularJS libraries and features to Angular 16-compatible libraries and features:
    {{ library_mappings }}

  ignore_file_paths:
    - 'gulpfile.js'
    - 'LICENSE'
    - '**/test/**'
    - 'test/**'
    - 'package.json'
    - '.gitignore'
    - 'tsconfig.json'
    - 'README.md'
    - '**/{{ angular_directory }}'
    - '**/{{ angular_directory }}/**'
    - '*.config$'
    - '**/public/**'
    - '**/lib/**'

steps:
  - name: Create Angular app
    tools:
      - name: run_terminal_command
        arguments:
          command: 'npx @angular/cli@latest new {{ angular_directory }} --routing --style=scss'

  - name: Install dependencies
    tools:
      - name: for_each
        items: '{{ dependencies }}'
        each_item:
          item_name: dependency
          tools:
            - name: run_terminal_command
              arguments:
                path_to_directory: '{{ angular_directory }}'
                command: 'npm install {{ dependency }} --save'
      - name: for_each
        items: '{{ dev_dependencies }}'
        each_item:
          item_name: dependency
          tools:
            - name: run_terminal_command
              arguments:
                path_to_directory: '{{ angular_directory }}'
                command: 'npm install {{ dependency }} --save-dev'

  - name: Update tsconfig to be stricter
    tools:
      - name: find_files_by_name_with_regex
        arguments:
          find_file_name_pattern: 'tsconfig\.json'
        returns: tsconfig_file_paths
      - name: get_first
        arguments:
          array: '{{ tsconfig_file_paths }}'
        returns: tsconfig_file_path
      - name: edit_json
        arguments:
          path_to_file: '{{ tsconfig_file_path }}'
          json_path: 'compilerOptions'
          action: 'add'
          values:
            - key: 'noUnusedLocals'
              value: true
            - key: 'noUnusedParameters'
              value: true

  - name: Build a dependency graph of the codebase
    tools:
      - name: build_dependency_graph_with_ai
        arguments:
          exclude_path_patterns: '{{ ignore_file_paths }}'

  - name: Convert files in order
    tools:
      - name: get_files_in_topological_order
        arguments:
          top_down: true
        returns: conversion_order_by_level

      - name: for_each
        items: '{{ conversion_order_by_level }}'
        each_item:
          item_name: level
          tools:
            - name: async_each
              items: '{{ level }}'
              each_item:
                item_name: file_to_convert
                tools:
                  - name: get_content_from_file
                    arguments:
                      path_to_file: '{{ file_to_convert }}'
                    returns: file_contents

                  - name: get_file_long_context
                    arguments:
                      path_to_file: '{{ file_to_convert }}'
                      depth: '{{ context_depth }}'
                      graph_type: 'embedding'
                    returns: file_context

                  - name: get_file_name
                    arguments:
                      path_to_file: '{{ file_to_convert }}'
                      include_extension: false
                    returns: file_name

                  # - name: hash_string
                  #   arguments:
                  #     input_string: '{{ file_to_convert }}'
                  #     max_length: 10
                  #   returns: file_hash

                  - name: switch
                    content: |
                      ## {{ file_to_convert }}
                      ```
                      {{ file_contents }}
                      ```
                    cases:
                      # Try to update the index.html file
                      - condition_prompt: Is this the root entry point template file for the AngularJS app? Typically, this is named something like `index.html`, has lots of imports, and sometimes has an `ng-app` directive.
                        break: true
                        tools:
                          - name: chunk_prompt
                            arguments:
                              contents:
                                - priority: 3
                                  content: '{{ file_context }}'
                                - priority: 2
                                  content: |
                                    AngularJS Code to convert to Angular ({{ file_to_convert }}):
                                    ```
                                    {{ file_contents }}
                                    ```
                                - priority: 1
                                  content: |
                                    # Instructions
                                    - Convert the AngularJS template code to be compatible with Angular and MERGE that code with the existing content in the file.
                                    - Do not just concatenate converted parts together; you should rewrite so that the code is DRY without losing or changing any functionality.
                                    - If there are no changes to make, return an empty code block (``````).
                                    - The resulting code should be a valid entry point template file for Angular.

                                    {{ transformation_notes }}
                            returns: chunked_prompts

                          - name: edit_file_long_context
                            arguments:
                              path_to_file: '{{ entry_file_path_template }}'
                              edit_prompts: '{{ chunked_prompts }}'

                          - name: mark_files_converted_in_context
                            arguments:
                              done_converting: true
                              old_file_path: '{{ file_to_convert }}'
                              new_file_paths:
                                - '{{ entry_file_path_template }}'

                      # Check for root entry point logic file
                      - condition_prompt: Is this the root entry point logic file for the AngularJS app? Typically, this is named something like `main.js` or `app.js`.
                        break: true
                        tools:
                          - name: chunk_prompt
                            arguments:
                              contents:
                                - priority: 3
                                  content: '{{ file_context }}'
                                - priority: 2
                                  content: |
                                    AngularJS Code to convert to Angular ({{ file_to_convert }}):
                                    ```
                                    {{ file_contents }}
                                    ```
                                - priority: 1
                                  content: |
                                    # Instructions
                                    - Convert the AngularJS logic code to be compatible with Angular and MERGE that code with the existing content in the file.
                                    - Do not just concatenate converted parts together; you should rewrite so that the code is DRY without losing or changing any functionality.
                                    - If there are no changes to make, return an empty code block (``````).
                                    - The resulting code should be valid Angular code in TypeScript.

                                    {{ transformation_notes }}
                            returns: chunked_prompts

                          - name: edit_file_long_context
                            arguments:
                              path_to_file: '{{ entry_file_path }}'
                              edit_prompts: '{{ chunked_prompts }}'

                          - name: mark_files_converted_in_context
                            arguments:
                              done_converting: false
                              old_file_path: '{{ file_to_convert }}'
                              new_file_paths:
                                - '{{ entry_file_path }}'

                      # Templates
                      - condition_prompt: Is this file an AngularJS template? (is it an `.html` file?)
                        break: true
                        tools:
                          - name: chunk_prompt
                            arguments:
                              contents:
                                - priority: 3
                                  content: '{{ file_context }}'
                                - priority: 2
                                  content: |
                                    AngularJS Code to convert to Angular ({{ file_to_convert }}):
                                    ```
                                    {{ file_contents }}
                                    ```
                                - priority: 1
                                  content: |
                                    # Instructions
                                    - The component, controller, or directive ts logic file related to this template will be converted in a later step.

                                    {{ transformation_notes }}
                            returns: chunked_prompts

                          - name: make_variable
                            arguments:
                              value: '{{ components_directory }}/{{ file_name }}.html'
                            returns: template_file_path

                          - name: edit_file_long_context
                            arguments:
                              path_to_file: '{{ template_file_path }}'
                              edit_prompts: '{{ chunked_prompts }}'

                          - name: mark_files_converted_in_context
                            arguments:
                              done_converting: false
                              old_file_path: '{{ file_to_convert }}'
                              new_file_paths:
                                - '{{ template_file_path }}'

                      # Constants or Values
                      - condition_prompt: Does this file contain an AngularJS constant or value definition? (Does it contain `.constant(...)` or `.value(...)`?)
                        tools:
                          - name: find_content_in_file_with_ai
                            arguments:
                              path_to_file: '{{ file_to_convert }}'
                              find_context_prompt: 'Find and return JUST the name of each constant or value defined in the file. This is the string declared as the first argument to `.constant(...)` or `.value(...)`. If there are none, return an empty array.'
                              results_as_array: true
                            returns: constant_or_value_names
                          - name: async_each
                            items: '{{ constant_or_value_names }}'
                            returns_key: constant_file_path
                            each_item:
                              item_name: constant_or_value_name
                              tools:
                                - name: make_variable
                                  arguments:
                                    value: '{{ constants_directory }}/{{ constant_or_value_name }}.ts'
                                  returns: constant_file_path
                                - name: chunk_prompt
                                  arguments:
                                    contents:
                                      - priority: 3
                                        content: '{{ file_context }}'
                                      - priority: 2
                                        content: |
                                          AngularJS Code ({{ file_to_convert }}):
                                          ```
                                          {{ file_contents }}
                                          ```
                                      - priority: 1
                                        content: |
                                          # Instructions
                                          - Extract `{{ constant_or_value_name }}` from the above AngularJS code and convert it to a TypeScript constant for Angular.
                                          - Only extract and convert `{{ constant_or_value_name }}` and its relevant information to make it work in Angular on its own. Ignore anything else.
                                          - The resulting file will be created at {{ constant_file_path }}.
                                          - Constants should be properly exported and can be imported and used in other files.
                                          - Use appropriate Angular patterns and modules.

                                          {{ transformation_notes }}
                                  returns: chunked_prompts

                                - name: edit_file_long_context
                                  arguments:
                                    path_to_file: '{{ constant_file_path }}'
                                    edit_prompts: '{{ chunked_prompts }}'
                            returns: file_paths

                          - name: mark_files_converted_in_context
                            arguments:
                              done_converting: false
                              old_file_path: '{{ file_to_convert }}'
                              new_file_paths: '{{ file_paths }}'

                      # Filters to Pipes
                      - condition_prompt: Does this file contain an AngularJS filter definition? (Does it contain `.filter(...)`?)
                        tools:
                          - name: find_content_in_file_with_ai
                            arguments:
                              path_to_file: '{{ file_to_convert }}'
                              find_context_prompt: 'Find and return JUST the name of each filter defined in the file. This is the string declared as the first argument to `.filter(...)`. If there are none, return an empty array.'
                              results_as_array: true
                            returns: filter_names
                          - name: async_each
                            items: '{{ filter_names }}'
                            returns_key: pipe_file_path
                            each_item:
                              item_name: filter_name
                              tools:
                                - name: make_variable
                                  arguments:
                                    value: '{{ pipes_directory }}/{{ filter_name }}.pipe.ts'
                                  returns: pipe_file_path
                                - name: chunk_prompt
                                  arguments:
                                    contents:
                                      - priority: 3
                                        content: '{{ file_context }}'
                                      - priority: 2
                                        content: |
                                          AngularJS Code ({{ file_to_convert }}):
                                          ```
                                          {{ file_contents }}
                                          ```
                                      - priority: 1
                                        content: |
                                          # Instructions
                                          - Extract `{{ filter_name }}` from the above AngularJS code and convert it to an Angular pipe.
                                          - Only extract and convert `{{ filter_name }}` and its relevant information to make it work in Angular on its own. Ignore anything else.
                                          - The resulting file will be created at {{ pipe_file_path }}.
                                          - Pipes should be properly decorated with `@Pipe` and exported.

                                          {{ transformation_notes }}
                                  returns: chunked_prompts

                                - name: edit_file_long_context
                                  arguments:
                                    path_to_file: '{{ pipe_file_path }}'
                                    edit_prompts: '{{ chunked_prompts }}'
                            returns: file_paths

                          - name: mark_files_converted_in_context
                            arguments:
                              done_converting: false
                              old_file_path: '{{ file_to_convert }}'
                              new_file_paths: '{{ file_paths }}'

                      # Services
                      - condition_prompt: Does this file contain an AngularJS service definition? (Does it contain `.service(...)` or `.factory(...)`, or `.provider(...)`?)
                        tools:
                          - name: find_content_in_file_with_ai
                            arguments:
                              path_to_file: '{{ file_to_convert }}'
                              find_context_prompt: 'Find and return JUST the name of each service defined in the file. This is the string declared as the first argument to `.service(...)` or `.factory(...)`, or `.provider(...)`. If there are none, return an empty array.'
                              results_as_array: true
                            returns: service_names
                          - name: async_each
                            items: '{{ service_names }}'
                            returns_key: service_file_path
                            each_item:
                              item_name: service_name
                              tools:
                                - name: make_variable
                                  arguments:
                                    value: '{{ services_directory }}/{{ service_name }}.service.ts'
                                  returns: service_file_path
                                - name: chunk_prompt
                                  arguments:
                                    contents:
                                      - priority: 3
                                        content: '{{ file_context }}'
                                      - priority: 2
                                        content: |
                                          AngularJS Code ({{ file_to_convert }}):
                                          ```
                                          {{ file_contents }}
                                          ```
                                      - priority: 1
                                        content: |
                                          # Instructions
                                          - Extract `{{ service_name }}` from the above AngularJS code and convert it to an Angular service.
                                          - Only extract and convert `{{ service_name }}` and its relevant information to make it work in Angular on its own. Ignore anything else.
                                          - The resulting file will be created at {{ service_file_path }}.
                                          - Services should be properly decorated with `@Injectable()` and exported.

                                          {{ transformation_notes }}
                                  returns: chunked_prompts

                                - name: edit_file_long_context
                                  arguments:
                                    path_to_file: '{{ service_file_path }}'
                                    edit_prompts: '{{ chunked_prompts }}'
                            returns: file_paths

                          - name: mark_files_converted_in_context
                            arguments:
                              done_converting: false
                              old_file_path: '{{ file_to_convert }}'
                              new_file_paths: '{{ file_paths }}'

                      # Configs and Routes
                      - condition_prompt: Does this file contain an AngularJS config definition (does it contain `.config(...)`) or AngularJS routes definitions (`$routeProvider.when(...)`, `$stateProvider.state(...)`, or some obvious URL paths that should be included in the Angular Router)?
                        tools:
                          - name: find_content_in_file_with_ai
                            arguments:
                              path_to_file: '{{ file_to_convert }}'
                              find_context_prompt: 'Find and return JUST the name of each config defined in the file. This is the string declared as the first argument to `.config(...)`. If there is no name defined, use the name of the function or object passed into `.config(...)`. If it is defined inline, invent a name for the function or object and use that name. If there are none, return an empty array.'
                              results_as_array: true
                            returns: config_names
                          - name: async_each
                            items: '{{ config_names }}'
                            returns_key: file_paths
                            each_item:
                              item_name: config_name
                              tools:
                                - name: ask_question
                                  arguments:
                                    question_prompt: |
                                      Does the config `{{ config_name }}` contain routes definitions that should be included in the Angular Router? Does it contain `$routeProvider.when(...)`, `$stateProvider.state(...)`, or any obvious URL paths?
                                      ```
                                      {{ file_contents }}
                                      ```
                                  returns: has_routes
                                - name: if_else
                                  condition: '{{ has_routes }}'
                                  returns_key: module_file_path
                                  if:
                                    tools:
                                      # Convert to routing module
                                      - name: chunk_prompt
                                        arguments:
                                          contents:
                                            - priority: 3
                                              content: '{{ file_context }}'
                                            - priority: 2
                                              content: |
                                                AngularJS Code to convert to Angular ({{ file_to_convert }}):
                                                ```
                                                {{ file_contents }}
                                                ```
                                            - priority: 1
                                              content: |
                                                # Instructions
                                                - Extract the route definitions from the above AngularJS code specific to `{{ config_name }}`.
                                                - Only consider the route definitions and related variables for `{{ config_name }}`. Ignore all other code.
                                                - Convert the extracted route code to use Angular Router.
                                                - Ensure all previous functionality remains along with the new logic.
                                                - Rewrite the code to be DRY without losing functionality. The result should be a valid Angular routing module.
                                                - If there is no convertible code, return an empty string.
                                                - Do not implement anything other than the router. Other logic will be converted in different steps.

                                                {{ transformation_notes }}
                                        returns: chunked_prompts
                                      - name: make_variable
                                        arguments:
                                          value: '{{ modules_directory }}/{{ config_name }}.routing.module.ts'
                                        returns: module_file_path
                                      - name: edit_file_long_context
                                        arguments:
                                          path_to_file: '{{ module_file_path }}'
                                          edit_prompts: '{{ chunked_prompts }}'
                                  else:
                                    tools:
                                      # Convert to Angular module
                                      - name: chunk_prompt
                                        arguments:
                                          contents:
                                            - priority: 3
                                              content: '{{ file_context }}'
                                            - priority: 2
                                              content: |
                                                AngularJS Code ({{ file_to_convert }}):
                                                ```
                                                {{ file_contents }}
                                                ```
                                            - priority: 1
                                              content: |
                                                # Instructions
                                                - Extract `{{ config_name }}` from the above AngularJS code and convert it to an Angular module.
                                                - Only extract and convert `{{ config_name }}` and its relevant information. Ignore anything else.
                                                - The resulting file will be created at {{ module_file_path }}.
                                                - Modules should be decorated with `@NgModule` and include declarations, imports, providers, and exports as necessary.

                                                {{ transformation_notes }}
                                        returns: chunked_prompts
                                      - name: make_variable
                                        arguments:
                                          value: '{{ modules_directory }}/{{ config_name }}.module.ts'
                                        returns: module_file_path
                                      - name: edit_file_long_context
                                        arguments:
                                          path_to_file: '{{ module_file_path }}'
                                          edit_prompts: '{{ chunked_prompts }}'
                                  returns: file_paths
                            returns: all_file_paths

                          - name: mark_files_converted_in_context
                            arguments:
                              done_converting: false
                              old_file_path: '{{ file_to_convert }}'
                              new_file_paths: '{{ all_file_paths }}'

                      # Directives
                      - condition_prompt: Does this file contain an AngularJS directive definition? (Does it contain `.directive(...)`?)
                        tools:
                          - name: find_content_in_file_with_ai
                            arguments:
                              path_to_file: '{{ file_to_convert }}'
                              find_context_prompt: 'Find and return JUST the name of each directive defined in the file. If there are none, return an empty array.'
                              results_as_array: true
                            returns: directive_names
                          - name: async_each
                            items: '{{ directive_names }}'
                            returns_key: directive_file_path
                            each_item:
                              item_name: directive_name
                              tools:
                                - name: ask_question
                                  arguments:
                                    question_prompt: |
                                      Does the directive `{{ directive_name }}` have a template? It does if it references some other external html file or defines its template inline.
                                      ```
                                      {{ file_contents }}
                                      ```
                                  returns: has_template
                                - name: if_else
                                  condition: '{{ has_template }}'
                                  returns_key: result_file_path
                                  if:
                                    tools:
                                      # Directives with templates become components
                                      - name: make_variable
                                        arguments:
                                          value: '{{ components_directory }}/{{ directive_name }}.component.ts'
                                        returns: result_file_path
                                      - name: chunk_prompt
                                        arguments:
                                          contents:
                                            - priority: 3
                                              content: '{{ file_context }}'
                                            - priority: 2
                                              content: |
                                                AngularJS Code ({{ file_to_convert }}):
                                                ```
                                                {{ file_contents }}
                                                ```
                                            - priority: 1
                                              content: |
                                                # Instructions
                                                - Extract `{{ directive_name }}` from the above AngularJS code and convert it to an Angular component.
                                                - The resulting file will be created at: `{{ result_file_path }}`
                                                - Components should be properly decorated with `@Component` and include the template and styles as necessary.
                                                - Only convert the typescript logic. Do not convert html templates and css styles unless they are defined inline.
                                                - Inputs should always have an `!` after them to denote they are initialized elsewhere. Example: `@Input() myInput!: string;`. `@Input() myInput: string;` will throw errors.
                                                - You should always export a single default class from the component: `export default class ...`.

                                                {{ transformation_notes }}
                                        returns: chunked_prompts

                                      - name: edit_file_long_context
                                        arguments:
                                          path_to_file: '{{ result_file_path }}'
                                          edit_prompts: '{{ chunked_prompts }}'
                                  else:
                                    tools:
                                      # This must be a pure directive
                                      - name: make_variable
                                        arguments:
                                          value: '{{ directives_directory }}/{{ directive_name }}.directive.ts'
                                        returns: result_file_path
                                      - name: chunk_prompt
                                        arguments:
                                          contents:
                                            - priority: 3
                                              content: '{{ file_context }}'
                                            - priority: 2
                                              content: |
                                                AngularJS Code ({{ file_to_convert }}):
                                                ```
                                                {{ file_contents }}
                                                ```
                                            - priority: 1
                                              content: |
                                                # Instructions
                                                - Extract `{{ directive_name }}` from the above AngularJS code and convert it to an Angular directive.
                                                - The resulting file will be created at: `{{ result_file_path }}`
                                                - Directives should be properly decorated with `@Directive` and exported.
                                                - Inputs should always have an `!` after them to denote they are initialized elsewhere. Example: `@Input() myInput!: string;`. `@Input() myInput: string;` will throw errors.

                                                {{ transformation_notes }}
                                        returns: chunked_prompts

                                      - name: edit_file_long_context
                                        arguments:
                                          path_to_file: '{{ result_file_path }}'
                                          edit_prompts: '{{ chunked_prompts }}'
                                  returns: directive_file_path
                            returns: file_paths

                          - name: mark_files_converted_in_context
                            arguments:
                              done_converting: false
                              old_file_path: '{{ file_to_convert }}'
                              new_file_paths: '{{ file_paths }}'

                      # Controllers to Components
                      - condition_prompt: Does this file contain an AngularJS controller/component definition? (Does it contain `.controller(...)` or `.component(...)`?)
                        tools:
                          - name: find_content_in_file_with_ai
                            arguments:
                              path_to_file: '{{ file_to_convert }}'
                              find_context_prompt: 'Find and return JUST the name of each controller/component defined in the file. This is the string declared as the first argument to `.controller(...)` or `.component(...)`. If there are none, return an empty array.'
                              results_as_array: true
                            returns: controller_names
                          - name: async_each
                            items: '{{ controller_names }}'
                            returns_key: component_file_path
                            each_item:
                              item_name: controller_name
                              tools:
                                - name: make_variable
                                  arguments:
                                    value: '{{ components_directory }}/{{ controller_name }}.component.ts'
                                  returns: component_file_path
                                - name: chunk_prompt
                                  arguments:
                                    contents:
                                      - priority: 3
                                        content: '{{ file_context }}'
                                      - priority: 2
                                        content: |
                                          AngularJS Code ({{ file_to_convert }}):
                                          ```
                                          {{ file_contents }}
                                          ```
                                      - priority: 1
                                        content: |
                                          # Instructions
                                          - Extract `{{ controller_name }}` from the above AngularJS code and convert it to an Angular component. that references the templateHTML file if necessary.
                                          - Only extract and convert `{{ controller_name }}` and its relevant information to make it work in Angular on its own. Ignore anything else.
                                          - The resulting files will be created at: `{{ component_file_path }}`
                                          - Components should be properly decorated with `@Component` and include the template and styles as necessary.
                                          - Only convert the controller/component typescript logic. Do not convert html templates and css styles unless they are defined inline.
                                          - Inputs should always have an `!` after them to denote they are initialized elsewhere. Example: `@Input() myInput!: string;`. `@Input() myInput: string;` will throw errors.

                                          {{ transformation_notes }}
                                  returns: chunked_prompts

                                - name: edit_file_long_context
                                  arguments:
                                    path_to_file: '{{ component_file_path }}'
                                    edit_prompts: '{{ chunked_prompts }}'
                            returns: file_paths

                          - name: mark_files_converted_in_context
                            arguments:
                              done_converting: false
                              old_file_path: '{{ file_to_convert }}'
                              new_file_paths: '{{ file_paths }}'

                    # Default case
                    default:
                      tools:
                        - name: echo_one
                          arguments:
                            echo_arg: 'Doing nothing with {{ file_to_convert }}'

                  # Mark file as converted
                  - name: mark_files_converted_in_context
                    arguments:
                      done_converting: true
                      old_file_path: '{{ file_to_convert }}'

  - name: Combine Routes into app-routing.module.ts
    tools:
      - name: find_files_by_name_with_regex
        arguments:
          path_to_directory: '{{ modules_directory }}'
          find_file_name_pattern: '.*\.routing\.module\.ts$'
        returns: routing_module_paths

      - name: get_content_from_files
        arguments:
          paths_to_files: '{{ routing_module_paths }}'
        returns: routing_module_contents

      - name: edit_file
        arguments:
          path_to_file: '{{ app_directory }}/app.routes.ts'
          edit_prompt: |
            The below code could contain Angular Router configurations. You should extract and add any routes directly into the `app.routes.ts` file, in addition to adding any comments that start with `// Converted from` that are present at the top of the file we are extracting from. Be meticulous and precise. Do not hallucinate.

            ## Routing Module Files
            ```
            {{ routing_module_contents }}
            ```

      # Delete routing modules now that they have been extracted into app.routes.ts
      - name: for_each
        items: '{{ routing_module_paths }}'
        each_item:
          item_name: routing_module_path
          tools:
            - name: delete_file_or_folder
              arguments:
                path_to_target: '{{ routing_module_path }}'

  - name: Copy static assets
    tools:
      - name: find_files_by_name_with_regex
        arguments:
          path_to_directory: 'src'
          find_file_name_pattern: .*\.(css|svg|png|jpg|jpeg|eot|ttf|otf|woff|woff2|gif|bmp|webp|webm|mp3)$
        returns: static_asset_files
      - name: for_each
        items: '{{ static_asset_files }}'
        each_item:
          item_name: static_file
          tools:
            - name: copy_file
              arguments:
                source_path: '{{ static_file }}'
                destination_path: '{{ assets_directory }}/{{ static_file }}'
              returns: copied_file_path

  - name: Add CSS imports to styles.css
    tools:
      - name: find_files_by_name_with_regex
        arguments:
          path_to_directory: '{{ assets_directory }}'
          find_file_name_pattern: '.*\.css$'
        returns: css_files
      - name: edit_file
        arguments:
          path_to_file: '{{ src_directory }}/styles.css'
          edit_prompt: |
            Add CSS imports at the top of the file for the following CSS files:
            ```
            {{ css_files }}
            ```
            Use the appropriate syntax to import CSS files in Angular `styles.css`.

  - name: Resolve import paths
    tools:
      - name: resolve_import_paths
        arguments:
          path_to_directory: '{{ angular_directory }}'

Last updated