angular / angular-cli

CLI tool for Angular
https://cli.angular.io
MIT License
26.74k stars 11.98k forks source link

Wildcard imports without an extension are not supported in application builder #26615

Open tsteuwer-accesso opened 9 months ago

tsteuwer-accesso commented 9 months ago

Which @angular/* package(s) are the source of the bug?

Don't known / other

Is this a regression?

Yes

Description

We have a set of auto-generated files that contain locale information for countries that's based off of CLDR information.

Example File

Filename: src/app/auto-generated/countries/XX.ts` where XX is the locale id (e.g. `en-us`).
export const data: [string, string][] = [['US', 'United States'], ['CA', 'Canada'], ...];

We used a Resolver class to retrieve this information.

export class CountryResolver implements Resolver<CountriesProperties[]> {
 constructor(@Inject(LOCALE_ID) private readonly localeId: string) {}

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<Countries> {
    return this.getCountries(this.localeId);
  }

  getCountries(localeId: string): Promise<CountryProperties[]> {
    const locale = toLower(localeId);
    // Attempt to import locales, otherwise import default locale 'en'
    return import(`../../../auto-generated/countries/${locale}`)
      .then(({ data } => resolve(data))
      .catch(() => import(`../../../auto-generated/countries/en`))
      .then(({ data } => resolve(data)));
  }

This has worked, even in Angular 15.

I have just upgraded to Angular 17, this no longer works. I now get the following error:

 [ERROR] File 'src/app/auto-generated/countries/en.d.ts' is missing from the TypeScript compilation. [plugin angular-compiler]
    src/app/pages/input/input.resolve.ts:4:16:
      4 │ ...urn import(`../../auto-generated/countries/${locale}...
        ╵               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  Ensure the file is part of the TypeScript program via the 'files' or 'include' property.

It outputs that error for every file name in that directory.

I have tried adding the following to tsconfig.app.json as well as tsconfig.json but it doesn't do anything:

include: ['src/app/auto-generated/**/*.ts']

Please provide a link to a minimal reproduction of the bug

https://stackblitz.com/edit/stackblitz-starters-gzddrv?file=package.json

Please provide the exception or error you saw

[ERROR] File 'src/app/auto-generated/countries/zh-hant.d.ts' is missing from the TypeScript compilation. [plugin angular-compiler]

    src/app/pages/input/input.resolve.ts:4:16:
      4 │ ...urn import(`../../auto-generated/countries/${locale}...
        ╵               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  Ensure the file is part of the TypeScript program via the 'files' or 'include' property.

Please provide the environment you discovered this bug in (run ng version)

Angular CLI: 17.0.2
Node: 18.16.0
Package Manager: npm 9.5.1
OS: linux x64

Angular: 17.0.4
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, platform-server
... router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1700.2
@angular-devkit/build-angular   17.0.2
@angular-devkit/core            17.0.2
@angular-devkit/schematics      17.0.2
@angular/cli                    17.0.2
@angular/ssr                    17.0.2
@schematics/angular             17.0.2
ng-packagr                      17.0.2
rxjs                            7.8.1
typescript                      5.2.2
zone.js                         0.14.2

Anything else?

I have also tried just converting the files to JSON, placing them in the assets folder and importing those via

import(`../../assets/path/to/${localeId}.json?raw`)

Btut I always get a console error saying that ../../assets/path/to/file/en.json doesn't exist.

I ended up just using fetch with the files in the assets folder, however, it looks like this might not work with SSR.

clydin commented 9 months ago

This doesn't solve the dynamic import issue you are having but that information is available directly from the browser via the Intl API. For display names: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DisplayNames

As to the import errors, it appears to be resolving to .d.ts files but if there are .ts files then .d.ts files shouldn't exist. Can you provide a sample of the filenames in the directory for some locales? A minimal reproduction would, however, be ideal to further investigate the issue.

montella1507 commented 9 months ago

I think this is because of switch to ESBUILD. As far as i know, ESBUILD is not so good in code splittig as webpack.

We have 1 ICON file (svg in ts) per file... ANd in WEBPACK it code splits the icons SO GREAT..but in ESBUILD powered angulae (in NG17) it packs ALL icons to 1 200kB CHUNK

This is why we have migrated back to Webpack build system...

montella1507 commented 9 months ago

ESBUILD:

image

Webpack: image

i have read, that Code-splitting in ESBuild is VERY limited and WIP.. https://esbuild.github.io/api/#splitting

I dont know why angular has switched to ESBUild by default as far as they know the importance of code splitting..

More info: https://makandracards.com/makandra/595482-code-splitting-in-esbuild-caveats-and-setup image

Also...We had to create Sencodary entry points for components used inside @defer() to make them really lazy loaded...

alan-agius4 commented 9 months ago

@montella1507, I'm a bit uncertain about the problem you've shared since the first image is too small for me to make out. Nonetheless, esbuild's approach to code splitting surpasses that of Webpack even though it is still marked as WIP in their docs as it performs code splitting and code motion based on usage, a capability Webpack lacks.

Regarding the highlighted point, it seems more like a powerful code motion feature than a drawback. This feature consolidates components used across multiple entry points into a shared chunk. If a component isn't used in one entry point, a separate chunk is created solely for that unshared component.

Since the original issue is unrelated to code splitting and code motion. I suggest that you file a new issue with a reproduction if you are experiencing any problem.

montella1507 commented 9 months ago

@alan-agius4 willing to elaborate? I can provide stats for big project - both esbuild and webpack.

Project is well designed in terms of code splitting..

alan-agius4 commented 9 months ago

@montella1507, again this is not the right issue to discuss your issue about code splitting, as the original issue being reported here is completely unrelated to code splitting.

Please file a new issue with a minimal reproduction that demonstrates the problem that you are experiencing.

tsteuwer-accesso commented 9 months ago

@clydin , here you go: https://stackblitz.com/edit/stackblitz-starters-gzddrv?file=package.json

Interestingly in Stackblitz, the console shows an error saying "Files not registered with SystemJS dynamic imports". I don't get that when I run it locally as you can see from the error messages above. I'm also using SSR so maybe that's why. But you can see it can't load the files regardless.

clydin commented 9 months ago

Thank you for the reproduction. That "Files not registered..." error seems to be StackBlitz related.

If you add "files/*.ts" to the include option in the tsconfig.app.json, that code should build successfully. I also tried locally and with that change, I got the following output:

Initial Chunk Files | Names         |  Raw Size | Estimated Transfer Size
main.js             | main          | 199.26 kB |                55.58 kB
chunk-RIKMN4LX.js   | -             | 547 bytes |               547 bytes
styles.css          | styles        |   0 bytes |                 0 bytes

                    | Initial Total | 199.79 kB |                56.11 kB

Lazy Chunk Files    | Names         |  Raw Size | Estimated Transfer Size
chunk-BH3KKJBR.js   | en-us         |  68 bytes |                68 bytes
chunk-DNIW2XDY.js   | ca            |  65 bytes |                65 bytes
chunk-6Z25HHN7.js   | en            |  65 bytes |                65 bytes

I would definitely recommend considering a switch to using the Intl API directly. This would avoid the need for the code autogeneration as well as the import/cache logic.

tsteuwer-accesso commented 9 months ago

@clydin , Thanks for taking a look. I also see the various chunks being generated for each file, but the issue is the errors that show up in the angular cli and in the browser saying it can't find them when trying to use them.

I've switched to the Intl for now, but this may be an issue where the data can't come from Intl :(

tsteuwer-accesso commented 9 months ago

When I get time, I'll try to come up with a github repo reproducing the error. I wonder if this only happens when using SSR becuase my repo does use SSR.

clydin commented 9 months ago

Upon further investigation, I believe i have tracked down what you are encountering. The lack of an extension within the import expression appears to be the problem. The dynamic import is assuming ESM browser behavior where a file extension is required. Changing the specifier text to ./files/${id}.ts allows it to work as intended in the browser. We will investigate a solution that potentially allows for the extensionless behavior.