diff --git a/e2e/angular-extensions/src/tailwind.test.ts b/e2e/angular-extensions/src/tailwind.test.ts
index 9ab473d82c..a8140e84e4 100644
--- a/e2e/angular-extensions/src/tailwind.test.ts
+++ b/e2e/angular-extensions/src/tailwind.test.ts
@@ -12,390 +12,387 @@ import {
} from '@nrwl/e2e/utils';
describe('Tailwind support', () => {
- it('tests are disabled', () => {
- expect(1).toEqual(1);
+ let project: string;
+
+ const defaultButtonBgColor = 'bg-blue-700';
+
+ const buildLibWithTailwind = {
+ name: uniq('build-lib-with-tailwind'),
+ buttonBgColor: 'bg-green-800',
+ };
+ const pubLibWithTailwind = {
+ name: uniq('pub-lib-with-tailwind'),
+ buttonBgColor: 'bg-red-900',
+ };
+
+ const spacing = {
+ root: {
+ sm: '2px',
+ md: '4px',
+ lg: '8px',
+ },
+ projectVariant1: {
+ sm: '1px',
+ md: '2px',
+ lg: '4px',
+ },
+ projectVariant2: {
+ sm: '4px',
+ md: '8px',
+ lg: '16px',
+ },
+ projectVariant3: {
+ sm: '8px',
+ md: '16px',
+ lg: '32px',
+ },
+ };
+
+ const createWorkspaceTailwindConfigFile = () => {
+ const tailwindConfigFile = 'tailwind.config.js';
+
+ const tailwindConfig = `module.exports = {
+ mode: 'jit',
+ purge: ['./apps/**/*.{html,ts}', './libs/**/*.{html,ts}'],
+ darkMode: false,
+ theme: {
+ spacing: {
+ sm: '${spacing.root.sm}',
+ md: '${spacing.root.md}',
+ lg: '${spacing.root.lg}',
+ },
+ },
+ variants: { extend: {} },
+ plugins: [],
+ };
+ `;
+
+ updateFile(tailwindConfigFile, tailwindConfig);
+ };
+
+ const createTailwindConfigFile = (
+ tailwindConfigFile = 'tailwind.config.js',
+ libSpacing: typeof spacing['projectVariant1']
+ ) => {
+ const tailwindConfig = `const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind');
+ const { join } = require('path');
+
+ module.exports = {
+ mode: 'jit',
+ purge: [
+ join(__dirname, 'src/**/*.{html,ts}'),
+ ...createGlobPatternsForDependencies(__dirname),
+ ],
+ darkMode: false,
+ theme: {
+ spacing: {
+ sm: '${libSpacing.sm}',
+ md: '${libSpacing.md}',
+ lg: '${libSpacing.lg}',
+ },
+ },
+ variants: { extend: {} },
+ plugins: [],
+ };
+ `;
+
+ updateFile(tailwindConfigFile, tailwindConfig);
+ };
+
+ const updateTailwindConfig = (
+ tailwindConfigPath: string,
+ projectSpacing: typeof spacing['root']
+ ) => {
+ const tailwindConfig = readFile(tailwindConfigPath);
+
+ const tailwindConfigUpdated = tailwindConfig.replace(
+ 'theme: {',
+ `theme: {
+ spacing: {
+ sm: '${projectSpacing.sm}',
+ md: '${projectSpacing.md}',
+ lg: '${projectSpacing.lg}',
+ },`
+ );
+
+ updateFile(tailwindConfigPath, tailwindConfigUpdated);
+ };
+
+ beforeAll(() => {
+ project = newProject();
+
+ // Create tailwind config in the workspace root
+ createWorkspaceTailwindConfigFile();
+ });
+
+ afterAll(() => cleanupProject());
+
+ describe('Libraries', () => {
+ const createLibComponent = (
+ lib: string,
+ buttonBgColor: string = defaultButtonBgColor
+ ) => {
+ updateFile(
+ `libs/${lib}/src/lib/foo.component.ts`,
+ `import { Component } from '@angular/core';
+
+ @Component({
+ selector: '${project}-foo',
+ template: '',
+ styles: [\`
+ .custom-btn {
+ @apply m-md p-sm;
+ }
+ \`]
+ })
+ export class FooComponent {}
+ `
+ );
+
+ updateFile(
+ `libs/${lib}/src/lib/${lib}.module.ts`,
+ `import { NgModule } from '@angular/core';
+ import { CommonModule } from '@angular/common';
+ import { FooComponent } from './foo.component';
+
+ @NgModule({
+ imports: [CommonModule],
+ declarations: [FooComponent],
+ exports: [FooComponent],
+ })
+ export class LibModule {}
+ `
+ );
+
+ updateFile(
+ `libs/${lib}/src/index.ts`,
+ `export * from './lib/foo.component';
+ export * from './lib/${lib}.module';
+ `
+ );
+ };
+
+ const assertLibComponentStyles = (
+ lib: string,
+ libSpacing: typeof spacing['root']
+ ) => {
+ const builtComponentContent = readFile(
+ `dist/libs/${lib}/esm2020/lib/foo.component.mjs`
+ );
+ let expectedStylesRegex = new RegExp(
+ `styles: \\[\\"\\.custom\\-btn(\\[_ngcontent\\-%COMP%\\])?{margin:${libSpacing.md};padding:${libSpacing.sm}}(\\\\n)?\\"\\]`
+ );
+
+ expect(builtComponentContent).toMatch(expectedStylesRegex);
+ };
+
+ it('should generate a buildable library with tailwind and build correctly', () => {
+ runCLI(
+ `generate @nrwl/angular:lib ${buildLibWithTailwind.name} --buildable --add-tailwind --no-interactive`
+ );
+ updateTailwindConfig(
+ `libs/${buildLibWithTailwind.name}/tailwind.config.js`,
+ spacing.projectVariant1
+ );
+ createLibComponent(
+ buildLibWithTailwind.name,
+ buildLibWithTailwind.buttonBgColor
+ );
+
+ runCLI(`build ${buildLibWithTailwind.name}`);
+
+ assertLibComponentStyles(
+ buildLibWithTailwind.name,
+ spacing.projectVariant1
+ );
+ });
+
+ it('should set up tailwind in a previously generated buildable library and build correctly', () => {
+ const buildLibSetupTailwind = uniq('build-lib-setup-tailwind');
+ runCLI(
+ `generate @nrwl/angular:lib ${buildLibSetupTailwind} --buildable --no-interactive`
+ );
+ runCLI(
+ `generate @nrwl/angular:setup-tailwind ${buildLibSetupTailwind} --no-interactive`
+ );
+ updateTailwindConfig(
+ `libs/${buildLibSetupTailwind}/tailwind.config.js`,
+ spacing.projectVariant2
+ );
+ createLibComponent(buildLibSetupTailwind);
+
+ runCLI(`build ${buildLibSetupTailwind}`);
+
+ assertLibComponentStyles(buildLibSetupTailwind, spacing.projectVariant2);
+ });
+
+ it('should correctly build a buildable library with a tailwind.config.js file in the project root or workspace root', () => {
+ const buildLibNoProjectConfig = uniq('build-lib-no-project-config');
+ runCLI(
+ `generate @nrwl/angular:lib ${buildLibNoProjectConfig} --buildable --no-interactive`
+ );
+ createTailwindConfigFile(
+ `libs/${buildLibNoProjectConfig}/tailwind.config.js`,
+ spacing.projectVariant3
+ );
+ createLibComponent(buildLibNoProjectConfig);
+
+ runCLI(`build ${buildLibNoProjectConfig}`);
+
+ assertLibComponentStyles(
+ buildLibNoProjectConfig,
+ spacing.projectVariant3
+ );
+
+ // remove tailwind.config.js file from the project root to test the one in the workspace root
+ removeFile(`libs/${buildLibNoProjectConfig}/tailwind.config.js`);
+
+ runCLI(`build ${buildLibNoProjectConfig}`);
+
+ assertLibComponentStyles(buildLibNoProjectConfig, spacing.root);
+ });
+
+ it('should generate a publishable library with tailwind and build correctly', () => {
+ runCLI(
+ `generate @nrwl/angular:lib ${pubLibWithTailwind.name} --publishable --add-tailwind --importPath=@${project}/${pubLibWithTailwind.name} --no-interactive`
+ );
+ updateTailwindConfig(
+ `libs/${pubLibWithTailwind.name}/tailwind.config.js`,
+ spacing.projectVariant1
+ );
+ createLibComponent(
+ pubLibWithTailwind.name,
+ pubLibWithTailwind.buttonBgColor
+ );
+
+ runCLI(`build ${pubLibWithTailwind.name}`);
+
+ assertLibComponentStyles(
+ pubLibWithTailwind.name,
+ spacing.projectVariant1
+ );
+ });
+
+ it('should set up tailwind in a previously generated publishable library and build correctly', () => {
+ const pubLibSetupTailwind = uniq('pub-lib-setup-tailwind');
+ runCLI(
+ `generate @nrwl/angular:lib ${pubLibSetupTailwind} --publishable --importPath=@${project}/${pubLibSetupTailwind} --no-interactive`
+ );
+ runCLI(
+ `generate @nrwl/angular:setup-tailwind ${pubLibSetupTailwind} --no-interactive`
+ );
+ updateTailwindConfig(
+ `libs/${pubLibSetupTailwind}/tailwind.config.js`,
+ spacing.projectVariant2
+ );
+ createLibComponent(pubLibSetupTailwind);
+
+ runCLI(`build ${pubLibSetupTailwind}`);
+
+ assertLibComponentStyles(pubLibSetupTailwind, spacing.projectVariant2);
+ });
+
+ it('should correctly build a publishable library with a tailwind.config.js file in the project root or workspace root', () => {
+ const pubLibNoProjectConfig = uniq('pub-lib-no-project-config');
+ runCLI(
+ `generate @nrwl/angular:lib ${pubLibNoProjectConfig} --publishable --importPath=@${project}/${pubLibNoProjectConfig} --no-interactive`
+ );
+ createTailwindConfigFile(
+ `libs/${pubLibNoProjectConfig}/tailwind.config.js`,
+ spacing.projectVariant3
+ );
+ createLibComponent(pubLibNoProjectConfig);
+
+ runCLI(`build ${pubLibNoProjectConfig}`);
+
+ assertLibComponentStyles(pubLibNoProjectConfig, spacing.projectVariant3);
+
+ // remove tailwind.config.js file from the project root to test the one in the workspace root
+ removeFile(`libs/${pubLibNoProjectConfig}/tailwind.config.js`);
+
+ runCLI(`build ${pubLibNoProjectConfig}`);
+
+ assertLibComponentStyles(pubLibNoProjectConfig, spacing.root);
+ });
+ });
+
+ describe('Applications', () => {
+ const updateAppComponent = (app: string) => {
+ updateFile(
+ `apps/${app}/src/app/app.component.html`,
+ ``
+ );
+
+ updateFile(
+ `apps/${app}/src/app/app.component.css`,
+ `.custom-btn {
+ @apply m-md p-sm;
+ }`
+ );
+ };
+
+ const readAppStylesBundle = (app: string) => {
+ const stylesBundlePath = listFiles(`dist/apps/${app}`).find((file) =>
+ file.startsWith('styles.')
+ );
+ const stylesBundle = readFile(`dist/apps/${app}/${stylesBundlePath}`);
+
+ return stylesBundle;
+ };
+
+ const assertAppComponentStyles = (
+ app: string,
+ appSpacing: typeof spacing['root']
+ ) => {
+ const mainBundlePath = listFiles(`dist/apps/${app}`).find((file) =>
+ file.startsWith('main.')
+ );
+ const mainBundle = readFile(`dist/apps/${app}/${mainBundlePath}`);
+ let expectedStylesRegex = new RegExp(
+ `styles:\\[\\"\\.custom\\-btn\\[_ngcontent\\-%COMP%\\]{margin:${appSpacing.md};padding:${appSpacing.sm}}\\"\\]`
+ );
+
+ expect(mainBundle).toMatch(expectedStylesRegex);
+ };
+
+ it('should build correctly and only output the tailwind utilities used', () => {
+ const appWithTailwind = uniq('app-with-tailwind');
+ runCLI(
+ `generate @nrwl/angular:app ${appWithTailwind} --add-tailwind --no-interactive`
+ );
+ updateTailwindConfig(
+ `apps/${appWithTailwind}/tailwind.config.js`,
+ spacing.projectVariant1
+ );
+ updateFile(
+ `apps/${appWithTailwind}/src/app/app.module.ts`,
+ `import { NgModule } from '@angular/core';
+ import { BrowserModule } from '@angular/platform-browser';
+ import { LibModule as LibModule1 } from '@${project}/${buildLibWithTailwind.name}';
+ import { LibModule as LibModule2 } from '@${project}/${pubLibWithTailwind.name}';
+
+ import { AppComponent } from './app.component';
+
+ @NgModule({
+ declarations: [AppComponent],
+ imports: [BrowserModule, LibModule1, LibModule2],
+ providers: [],
+ bootstrap: [AppComponent],
+ })
+ export class AppModule {}
+ `
+ );
+ updateAppComponent(appWithTailwind);
+
+ runCLI(`build ${appWithTailwind}`);
+
+ assertAppComponentStyles(appWithTailwind, spacing.projectVariant1);
+ let stylesBundle = readAppStylesBundle(appWithTailwind);
+ expect(stylesBundle).toContain('.text-white');
+ expect(stylesBundle).not.toContain('.text-black');
+ expect(stylesBundle).toContain(`.${buildLibWithTailwind.buttonBgColor}`);
+ expect(stylesBundle).toContain(`.${pubLibWithTailwind.buttonBgColor}`);
+ expect(stylesBundle).not.toContain(`.${defaultButtonBgColor}`);
+ });
});
- // let project: string;
- //
- // const defaultButtonBgColor = 'bg-blue-700';
- //
- // const buildLibWithTailwind = {
- // name: uniq('build-lib-with-tailwind'),
- // buttonBgColor: 'bg-green-800',
- // };
- // const pubLibWithTailwind = {
- // name: uniq('pub-lib-with-tailwind'),
- // buttonBgColor: 'bg-red-900',
- // };
- //
- // const spacing = {
- // root: {
- // sm: '2px',
- // md: '4px',
- // lg: '8px',
- // },
- // projectVariant1: {
- // sm: '1px',
- // md: '2px',
- // lg: '4px',
- // },
- // projectVariant2: {
- // sm: '4px',
- // md: '8px',
- // lg: '16px',
- // },
- // projectVariant3: {
- // sm: '8px',
- // md: '16px',
- // lg: '32px',
- // },
- // };
- //
- // const createWorkspaceTailwindConfigFile = () => {
- // const tailwindConfigFile = 'tailwind.config.js';
- //
- // const tailwindConfig = `module.exports = {
- // mode: 'jit',
- // purge: ['./apps/**/*.{html,ts}', './libs/**/*.{html,ts}'],
- // darkMode: false,
- // theme: {
- // spacing: {
- // sm: '${spacing.root.sm}',
- // md: '${spacing.root.md}',
- // lg: '${spacing.root.lg}',
- // },
- // },
- // variants: { extend: {} },
- // plugins: [],
- // };
- // `;
- //
- // updateFile(tailwindConfigFile, tailwindConfig);
- // };
- //
- // const createTailwindConfigFile = (
- // tailwindConfigFile = 'tailwind.config.js',
- // libSpacing: typeof spacing['projectVariant1']
- // ) => {
- // const tailwindConfig = `const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind');
- // const { join } = require('path');
- //
- // module.exports = {
- // mode: 'jit',
- // purge: [
- // join(__dirname, 'src/**/*.{html,ts}'),
- // ...createGlobPatternsForDependencies(__dirname),
- // ],
- // darkMode: false,
- // theme: {
- // spacing: {
- // sm: '${libSpacing.sm}',
- // md: '${libSpacing.md}',
- // lg: '${libSpacing.lg}',
- // },
- // },
- // variants: { extend: {} },
- // plugins: [],
- // };
- // `;
- //
- // updateFile(tailwindConfigFile, tailwindConfig);
- // };
- //
- // const updateTailwindConfig = (
- // tailwindConfigPath: string,
- // projectSpacing: typeof spacing['root']
- // ) => {
- // const tailwindConfig = readFile(tailwindConfigPath);
- //
- // const tailwindConfigUpdated = tailwindConfig.replace(
- // 'theme: {',
- // `theme: {
- // spacing: {
- // sm: '${projectSpacing.sm}',
- // md: '${projectSpacing.md}',
- // lg: '${projectSpacing.lg}',
- // },`
- // );
- //
- // updateFile(tailwindConfigPath, tailwindConfigUpdated);
- // };
- //
- // beforeAll(() => {
- // project = newProject();
- //
- // // Create tailwind config in the workspace root
- // createWorkspaceTailwindConfigFile();
- // });
- //
- // afterAll(() => cleanupProject());
- //
- // describe('Libraries', () => {
- // const createLibComponent = (
- // lib: string,
- // buttonBgColor: string = defaultButtonBgColor
- // ) => {
- // updateFile(
- // `libs/${lib}/src/lib/foo.component.ts`,
- // `import { Component } from '@angular/core';
- //
- // @Component({
- // selector: '${project}-foo',
- // template: '',
- // styles: [\`
- // .custom-btn {
- // @apply m-md p-sm;
- // }
- // \`]
- // })
- // export class FooComponent {}
- // `
- // );
- //
- // updateFile(
- // `libs/${lib}/src/lib/${lib}.module.ts`,
- // `import { NgModule } from '@angular/core';
- // import { CommonModule } from '@angular/common';
- // import { FooComponent } from './foo.component';
- //
- // @NgModule({
- // imports: [CommonModule],
- // declarations: [FooComponent],
- // exports: [FooComponent],
- // })
- // export class LibModule {}
- // `
- // );
- //
- // updateFile(
- // `libs/${lib}/src/index.ts`,
- // `export * from './lib/foo.component';
- // export * from './lib/${lib}.module';
- // `
- // );
- // };
- //
- // const assertLibComponentStyles = (
- // lib: string,
- // libSpacing: typeof spacing['root']
- // ) => {
- // const builtComponentContent = readFile(
- // `dist/libs/${lib}/esm2020/lib/foo.component.mjs`
- // );
- // let expectedStylesRegex = new RegExp(
- // `styles: \\[\\"\\.custom\\-btn(\\[_ngcontent\\-%COMP%\\])?{margin:${libSpacing.md};padding:${libSpacing.sm}}(\\\\n)?\\"\\]`
- // );
- //
- // expect(builtComponentContent).toMatch(expectedStylesRegex);
- // };
- //
- // it('should generate a buildable library with tailwind and build correctly', () => {
- // runCLI(
- // `generate @nrwl/angular:lib ${buildLibWithTailwind.name} --buildable --add-tailwind --no-interactive`
- // );
- // updateTailwindConfig(
- // `libs/${buildLibWithTailwind.name}/tailwind.config.js`,
- // spacing.projectVariant1
- // );
- // createLibComponent(
- // buildLibWithTailwind.name,
- // buildLibWithTailwind.buttonBgColor
- // );
- //
- // runCLI(`build ${buildLibWithTailwind.name}`);
- //
- // assertLibComponentStyles(
- // buildLibWithTailwind.name,
- // spacing.projectVariant1
- // );
- // });
- //
- // it('should set up tailwind in a previously generated buildable library and build correctly', () => {
- // const buildLibSetupTailwind = uniq('build-lib-setup-tailwind');
- // runCLI(
- // `generate @nrwl/angular:lib ${buildLibSetupTailwind} --buildable --no-interactive`
- // );
- // runCLI(
- // `generate @nrwl/angular:setup-tailwind ${buildLibSetupTailwind} --no-interactive`
- // );
- // updateTailwindConfig(
- // `libs/${buildLibSetupTailwind}/tailwind.config.js`,
- // spacing.projectVariant2
- // );
- // createLibComponent(buildLibSetupTailwind);
- //
- // runCLI(`build ${buildLibSetupTailwind}`);
- //
- // assertLibComponentStyles(buildLibSetupTailwind, spacing.projectVariant2);
- // });
- //
- // it('should correctly build a buildable library with a tailwind.config.js file in the project root or workspace root', () => {
- // const buildLibNoProjectConfig = uniq('build-lib-no-project-config');
- // runCLI(
- // `generate @nrwl/angular:lib ${buildLibNoProjectConfig} --buildable --no-interactive`
- // );
- // createTailwindConfigFile(
- // `libs/${buildLibNoProjectConfig}/tailwind.config.js`,
- // spacing.projectVariant3
- // );
- // createLibComponent(buildLibNoProjectConfig);
- //
- // runCLI(`build ${buildLibNoProjectConfig}`);
- //
- // assertLibComponentStyles(
- // buildLibNoProjectConfig,
- // spacing.projectVariant3
- // );
- //
- // // remove tailwind.config.js file from the project root to test the one in the workspace root
- // removeFile(`libs/${buildLibNoProjectConfig}/tailwind.config.js`);
- //
- // runCLI(`build ${buildLibNoProjectConfig}`);
- //
- // assertLibComponentStyles(buildLibNoProjectConfig, spacing.root);
- // });
- //
- // it('should generate a publishable library with tailwind and build correctly', () => {
- // runCLI(
- // `generate @nrwl/angular:lib ${pubLibWithTailwind.name} --publishable --add-tailwind --importPath=@${project}/${pubLibWithTailwind.name} --no-interactive`
- // );
- // updateTailwindConfig(
- // `libs/${pubLibWithTailwind.name}/tailwind.config.js`,
- // spacing.projectVariant1
- // );
- // createLibComponent(
- // pubLibWithTailwind.name,
- // pubLibWithTailwind.buttonBgColor
- // );
- //
- // runCLI(`build ${pubLibWithTailwind.name}`);
- //
- // assertLibComponentStyles(
- // pubLibWithTailwind.name,
- // spacing.projectVariant1
- // );
- // });
- //
- // it('should set up tailwind in a previously generated publishable library and build correctly', () => {
- // const pubLibSetupTailwind = uniq('pub-lib-setup-tailwind');
- // runCLI(
- // `generate @nrwl/angular:lib ${pubLibSetupTailwind} --publishable --importPath=@${project}/${pubLibSetupTailwind} --no-interactive`
- // );
- // runCLI(
- // `generate @nrwl/angular:setup-tailwind ${pubLibSetupTailwind} --no-interactive`
- // );
- // updateTailwindConfig(
- // `libs/${pubLibSetupTailwind}/tailwind.config.js`,
- // spacing.projectVariant2
- // );
- // createLibComponent(pubLibSetupTailwind);
- //
- // runCLI(`build ${pubLibSetupTailwind}`);
- //
- // assertLibComponentStyles(pubLibSetupTailwind, spacing.projectVariant2);
- // });
- //
- // it('should correctly build a publishable library with a tailwind.config.js file in the project root or workspace root', () => {
- // const pubLibNoProjectConfig = uniq('pub-lib-no-project-config');
- // runCLI(
- // `generate @nrwl/angular:lib ${pubLibNoProjectConfig} --publishable --importPath=@${project}/${pubLibNoProjectConfig} --no-interactive`
- // );
- // createTailwindConfigFile(
- // `libs/${pubLibNoProjectConfig}/tailwind.config.js`,
- // spacing.projectVariant3
- // );
- // createLibComponent(pubLibNoProjectConfig);
- //
- // runCLI(`build ${pubLibNoProjectConfig}`);
- //
- // assertLibComponentStyles(pubLibNoProjectConfig, spacing.projectVariant3);
- //
- // // remove tailwind.config.js file from the project root to test the one in the workspace root
- // removeFile(`libs/${pubLibNoProjectConfig}/tailwind.config.js`);
- //
- // runCLI(`build ${pubLibNoProjectConfig}`);
- //
- // assertLibComponentStyles(pubLibNoProjectConfig, spacing.root);
- // });
- // });
- //
- // describe('Applications', () => {
- // const updateAppComponent = (app: string) => {
- // updateFile(
- // `apps/${app}/src/app/app.component.html`,
- // ``
- // );
- //
- // updateFile(
- // `apps/${app}/src/app/app.component.css`,
- // `.custom-btn {
- // @apply m-md p-sm;
- // }`
- // );
- // };
- //
- // const readAppStylesBundle = (app: string) => {
- // const stylesBundlePath = listFiles(`dist/apps/${app}`).find((file) =>
- // file.startsWith('styles.')
- // );
- // const stylesBundle = readFile(`dist/apps/${app}/${stylesBundlePath}`);
- //
- // return stylesBundle;
- // };
- //
- // const assertAppComponentStyles = (
- // app: string,
- // appSpacing: typeof spacing['root']
- // ) => {
- // const mainBundlePath = listFiles(`dist/apps/${app}`).find((file) =>
- // file.startsWith('main.')
- // );
- // const mainBundle = readFile(`dist/apps/${app}/${mainBundlePath}`);
- // let expectedStylesRegex = new RegExp(
- // `styles:\\[\\"\\.custom\\-btn\\[_ngcontent\\-%COMP%\\]{margin:${appSpacing.md};padding:${appSpacing.sm}}\\"\\]`
- // );
- //
- // expect(mainBundle).toMatch(expectedStylesRegex);
- // };
- //
- // it('should build correctly and only output the tailwind utilities used', () => {
- // const appWithTailwind = uniq('app-with-tailwind');
- // runCLI(
- // `generate @nrwl/angular:app ${appWithTailwind} --add-tailwind --no-interactive`
- // );
- // updateTailwindConfig(
- // `apps/${appWithTailwind}/tailwind.config.js`,
- // spacing.projectVariant1
- // );
- // updateFile(
- // `apps/${appWithTailwind}/src/app/app.module.ts`,
- // `import { NgModule } from '@angular/core';
- // import { BrowserModule } from '@angular/platform-browser';
- // import { LibModule as LibModule1 } from '@${project}/${buildLibWithTailwind.name}';
- // import { LibModule as LibModule2 } from '@${project}/${pubLibWithTailwind.name}';
- //
- // import { AppComponent } from './app.component';
- //
- // @NgModule({
- // declarations: [AppComponent],
- // imports: [BrowserModule, LibModule1, LibModule2],
- // providers: [],
- // bootstrap: [AppComponent],
- // })
- // export class AppModule {}
- // `
- // );
- // updateAppComponent(appWithTailwind);
- //
- // runCLI(`build ${appWithTailwind}`);
- //
- // assertAppComponentStyles(appWithTailwind, spacing.projectVariant1);
- // let stylesBundle = readAppStylesBundle(appWithTailwind);
- // expect(stylesBundle).toContain('.text-white');
- // expect(stylesBundle).not.toContain('.text-black');
- // expect(stylesBundle).toContain(`.${buildLibWithTailwind.buttonBgColor}`);
- // expect(stylesBundle).toContain(`.${pubLibWithTailwind.buttonBgColor}`);
- // expect(stylesBundle).not.toContain(`.${defaultButtonBgColor}`);
- // });
- // });
});
diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/ng-package/entry-point/compile-ngc.transform.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/ng-package/entry-point/compile-ngc.transform.ts
index 82dcb6c5da..2fd67e7422 100644
--- a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/ng-package/entry-point/compile-ngc.transform.ts
+++ b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/ng-package/entry-point/compile-ngc.transform.ts
@@ -58,7 +58,7 @@ export const nxCompileNgcTransformFactory = (
declaration: true,
target: ts.ScriptTarget.ES2020,
},
- entryPoint.cache.stylesheetProcessor,
+ entryPoint.cache.stylesheetProcessor as any,
null,
options.watch
);
diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/ngc/compile-source-files.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/ngc/compile-source-files.ts
index 042c10b7c9..48bf448878 100644
--- a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/ngc/compile-source-files.ts
+++ b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/ngc/compile-source-files.ts
@@ -3,6 +3,7 @@
*
* Changes made:
* - Made sure ngccProcessor is optional.
+ * - Use custom cacheCompilerHost instead of the one provided by ng-packagr.
*/
import type {
@@ -17,15 +18,13 @@ import {
PackageNode,
} from 'ng-packagr/lib/ng-package/nodes';
import { NgccProcessor } from 'ng-packagr/lib/ngc/ngcc-processor';
-import { StylesheetProcessor } from 'ng-packagr/lib/styles/stylesheet-processor';
-import {
- augmentProgramWithVersioning,
- cacheCompilerHost,
-} from 'ng-packagr/lib/ts/cache-compiler-host';
+import { augmentProgramWithVersioning } from 'ng-packagr/lib/ts/cache-compiler-host';
import { ngccTransformCompilerHost } from 'ng-packagr/lib/ts/ngcc-transform-compiler-host';
import * as log from 'ng-packagr/lib/utils/log';
import { ngCompilerCli } from 'ng-packagr/lib/utils/ng-compiler-cli';
import * as ts from 'typescript';
+import { StylesheetProcessor } from '../styles/stylesheet-processor';
+import { cacheCompilerHost } from '../ts/cache-compiler-host';
export async function compileSourceFiles(
graph: BuildGraph,
diff --git a/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/ts/cache-compiler-host.ts b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/ts/cache-compiler-host.ts
new file mode 100644
index 0000000000..8a2b6e6eaf
--- /dev/null
+++ b/packages/angular/src/executors/ng-packagr-lite/ng-packagr-adjustments/ts/cache-compiler-host.ts
@@ -0,0 +1,219 @@
+/**
+ * Adapted from the original ng-packagr source.
+ *
+ * Changes made:
+ * - Changed filePath passed to the StylesheetProcessor.parse when is a .ts file and inlineStyleLanguage is set.
+ */
+
+import type { CompilerHost, CompilerOptions } from '@angular/compiler-cli';
+import { createHash } from 'crypto';
+import { FileCache } from 'ng-packagr/lib/file-system/file-cache';
+import { BuildGraph } from 'ng-packagr/lib/graph/build-graph';
+import { Node } from 'ng-packagr/lib/graph/node';
+import { EntryPointNode, fileUrl } from 'ng-packagr/lib/ng-package/nodes';
+import { ensureUnixPath } from 'ng-packagr/lib/utils/path';
+import * as path from 'path';
+import * as ts from 'typescript';
+import {
+ InlineStyleLanguage,
+ StylesheetProcessor,
+} from '../styles/stylesheet-processor';
+
+export function cacheCompilerHost(
+ graph: BuildGraph,
+ entryPoint: EntryPointNode,
+ compilerOptions: CompilerOptions,
+ moduleResolutionCache: ts.ModuleResolutionCache,
+ stylesheetProcessor?: StylesheetProcessor,
+ inlineStyleLanguage?: InlineStyleLanguage,
+ sourcesFileCache: FileCache = entryPoint.cache.sourcesFileCache
+): CompilerHost {
+ const compilerHost = ts.createIncrementalCompilerHost(compilerOptions);
+
+ const getNode = (fileName: string) => {
+ const nodeUri = fileUrl(ensureUnixPath(fileName));
+ let node = graph.get(nodeUri);
+
+ if (!node) {
+ node = new Node(nodeUri);
+ graph.put(node);
+ }
+
+ return node;
+ };
+
+ const addDependee = (fileName: string) => {
+ const node = getNode(fileName);
+ entryPoint.dependsOn(node);
+ };
+
+ return {
+ ...compilerHost,
+
+ // ts specific
+ fileExists: (fileName: string) => {
+ const cache = sourcesFileCache.getOrCreate(fileName);
+ if (cache.exists === undefined) {
+ cache.exists = compilerHost.fileExists.call(this, fileName);
+ }
+
+ return cache.exists;
+ },
+
+ getSourceFile: (fileName: string, languageVersion: ts.ScriptTarget) => {
+ addDependee(fileName);
+ const cache = sourcesFileCache.getOrCreate(fileName);
+ if (!cache.sourceFile) {
+ cache.sourceFile = compilerHost.getSourceFile.call(
+ this,
+ fileName,
+ languageVersion
+ );
+ }
+
+ return cache.sourceFile;
+ },
+
+ writeFile: (
+ fileName: string,
+ data: string,
+ writeByteOrderMark: boolean,
+ onError?: (message: string) => void,
+ sourceFiles?: ReadonlyArray
+ ) => {
+ if (fileName.endsWith('.d.ts')) {
+ sourceFiles.forEach((source) => {
+ const cache = sourcesFileCache.getOrCreate(source.fileName);
+ if (!cache.declarationFileName) {
+ cache.declarationFileName = ensureUnixPath(fileName);
+ }
+ });
+ } else {
+ fileName = fileName.replace(/\.js(\.map)?$/, '.mjs$1');
+ const outputCache = entryPoint.cache.outputCache;
+
+ outputCache.set(fileName, {
+ content: data,
+ version: createHash('sha256').update(data).digest('hex'),
+ });
+ }
+
+ compilerHost.writeFile.call(
+ this,
+ fileName,
+ data,
+ writeByteOrderMark,
+ onError,
+ sourceFiles
+ );
+ },
+
+ readFile: (fileName: string) => {
+ addDependee(fileName);
+ const cache = sourcesFileCache.getOrCreate(fileName);
+ if (cache.content === undefined) {
+ cache.content = compilerHost.readFile.call(this, fileName);
+ }
+
+ return cache.content;
+ },
+
+ resolveModuleNames: (moduleNames: string[], containingFile: string) => {
+ return moduleNames.map((moduleName) => {
+ const { resolvedModule } = ts.resolveModuleName(
+ moduleName,
+ ensureUnixPath(containingFile),
+ compilerOptions,
+ compilerHost,
+ moduleResolutionCache
+ );
+
+ return resolvedModule;
+ });
+ },
+
+ resourceNameToFileName: (
+ resourceName: string,
+ containingFilePath: string
+ ) => {
+ const resourcePath = path.resolve(
+ path.dirname(containingFilePath),
+ resourceName
+ );
+ const containingNode = getNode(containingFilePath);
+ const resourceNode = getNode(resourcePath);
+ containingNode.dependsOn(resourceNode);
+
+ return resourcePath;
+ },
+
+ readResource: async (fileName: string) => {
+ addDependee(fileName);
+
+ const cache = sourcesFileCache.getOrCreate(fileName);
+ if (cache.content === undefined) {
+ if (/(?:html?|svg)$/.test(path.extname(fileName))) {
+ // template
+ cache.content = compilerHost.readFile.call(this, fileName);
+ } else {
+ // stylesheet
+ cache.content = await stylesheetProcessor.process({
+ filePath: fileName,
+ content: compilerHost.readFile.call(this, fileName),
+ });
+ }
+
+ if (cache.content === undefined) {
+ throw new Error(`Cannot read file ${fileName}.`);
+ }
+
+ cache.exists = true;
+ }
+
+ return cache.content;
+ },
+ transformResource: async (data, context) => {
+ if (context.resourceFile || context.type !== 'style') {
+ return null;
+ }
+
+ if (inlineStyleLanguage) {
+ const key = createHash('sha1').update(data).digest('hex');
+ const fileName = `${context.containingFile}-${key}.${inlineStyleLanguage}`;
+ const cache = sourcesFileCache.getOrCreate(fileName);
+ if (cache.content === undefined) {
+ cache.content = await stylesheetProcessor.process({
+ filePath: context.containingFile, // @leosvelperez: changed from fileName
+ content: data,
+ });
+
+ const virtualFileNode = getNode(fileName);
+ const containingFileNode = getNode(context.containingFile);
+ virtualFileNode.dependsOn(containingFileNode);
+ }
+
+ cache.exists = true;
+
+ return { content: cache.content };
+ }
+
+ return null;
+ },
+ };
+}
+
+export function augmentProgramWithVersioning(program: ts.Program): void {
+ const baseGetSourceFiles = program.getSourceFiles;
+ program.getSourceFiles = function (...parameters) {
+ const files: readonly (ts.SourceFile & { version?: string })[] =
+ baseGetSourceFiles(...parameters);
+
+ for (const file of files) {
+ if (file.version === undefined) {
+ file.version = createHash('sha256').update(file.text).digest('hex');
+ }
+ }
+
+ return files;
+ };
+}
diff --git a/packages/angular/src/executors/package/ng-packagr-adjustments/ng-package/entry-point/compile-ngc.transform.ts b/packages/angular/src/executors/package/ng-packagr-adjustments/ng-package/entry-point/compile-ngc.transform.ts
index 7787be3fb7..9d3c47228b 100644
--- a/packages/angular/src/executors/package/ng-packagr-adjustments/ng-package/entry-point/compile-ngc.transform.ts
+++ b/packages/angular/src/executors/package/ng-packagr-adjustments/ng-package/entry-point/compile-ngc.transform.ts
@@ -14,13 +14,13 @@ import {
isEntryPoint,
isEntryPointInProgress,
} from 'ng-packagr/lib/ng-package/nodes';
-import { compileSourceFiles } from 'ng-packagr/lib/ngc/compile-source-files';
import { NgccProcessor } from 'ng-packagr/lib/ngc/ngcc-processor';
import { setDependenciesTsConfigPaths } from 'ng-packagr/lib/ts/tsconfig';
import { ngccCompilerCli } from 'ng-packagr/lib/utils/ng-compiler-cli';
import * as ora from 'ora';
import * as path from 'path';
import * as ts from 'typescript';
+import { compileSourceFiles } from '../../ngc/compile-source-files';
import { StylesheetProcessor as StylesheetProcessorClass } from '../../styles/stylesheet-processor';
import { NgPackagrOptions } from '../options.di';
@@ -85,7 +85,7 @@ export const compileNgcTransformFactory = (
declaration: true,
target: ts.ScriptTarget.ES2020,
},
- entryPoint.cache.stylesheetProcessor,
+ entryPoint.cache.stylesheetProcessor as any,
ngccProcessor,
options.watch
);
diff --git a/packages/angular/src/executors/package/ng-packagr-adjustments/ngc/compile-source-files.ts b/packages/angular/src/executors/package/ng-packagr-adjustments/ngc/compile-source-files.ts
new file mode 100644
index 0000000000..01be8118e8
--- /dev/null
+++ b/packages/angular/src/executors/package/ng-packagr-adjustments/ngc/compile-source-files.ts
@@ -0,0 +1,221 @@
+/**
+ * Adapted from the original ng-packagr source.
+ *
+ * Changes made:
+ * - Use custom cacheCompilerHost instead of the one provided by ng-packagr.
+ */
+
+import type {
+ CompilerOptions,
+ ParsedConfiguration,
+} from '@angular/compiler-cli';
+import { BuildGraph } from 'ng-packagr/lib/graph/build-graph';
+import {
+ EntryPointNode,
+ isEntryPointInProgress,
+ isPackage,
+ PackageNode,
+} from 'ng-packagr/lib/ng-package/nodes';
+import { NgccProcessor } from 'ng-packagr/lib/ngc/ngcc-processor';
+import { ngccTransformCompilerHost } from 'ng-packagr/lib/ts/ngcc-transform-compiler-host';
+import * as log from 'ng-packagr/lib/utils/log';
+import { ngCompilerCli } from 'ng-packagr/lib/utils/ng-compiler-cli';
+import * as ts from 'typescript';
+import { StylesheetProcessor } from '../styles/stylesheet-processor';
+import {
+ augmentProgramWithVersioning,
+ cacheCompilerHost,
+} from '../ts/cache-compiler-host';
+
+export async function compileSourceFiles(
+ graph: BuildGraph,
+ tsConfig: ParsedConfiguration,
+ moduleResolutionCache: ts.ModuleResolutionCache,
+ extraOptions?: Partial,
+ stylesheetProcessor?: StylesheetProcessor,
+ ngccProcessor?: NgccProcessor,
+ watch?: boolean
+) {
+ const { NgtscProgram, formatDiagnostics } = await ngCompilerCli();
+
+ const tsConfigOptions: CompilerOptions = {
+ ...tsConfig.options,
+ ...extraOptions,
+ };
+ const entryPoint: EntryPointNode = graph.find(isEntryPointInProgress());
+ const ngPackageNode: PackageNode = graph.find(isPackage);
+ const inlineStyleLanguage = ngPackageNode.data.inlineStyleLanguage;
+
+ const tsCompilerHost = ngccTransformCompilerHost(
+ cacheCompilerHost(
+ graph,
+ entryPoint,
+ tsConfigOptions,
+ moduleResolutionCache,
+ stylesheetProcessor,
+ inlineStyleLanguage
+ ),
+ tsConfigOptions,
+ ngccProcessor,
+ moduleResolutionCache
+ );
+
+ const cache = entryPoint.cache;
+ const sourceFileCache = cache.sourcesFileCache;
+
+ // Create the Angular specific program that contains the Angular compiler
+ const angularProgram = new NgtscProgram(
+ tsConfig.rootNames,
+ tsConfigOptions,
+ tsCompilerHost,
+ cache.oldNgtscProgram
+ );
+
+ const angularCompiler = angularProgram.compiler;
+ const { ignoreForDiagnostics, ignoreForEmit } = angularCompiler;
+
+ // SourceFile versions are required for builder programs.
+ // The wrapped host inside NgtscProgram adds additional files that will not have versions.
+ const typeScriptProgram = angularProgram.getTsProgram();
+ augmentProgramWithVersioning(typeScriptProgram);
+
+ let builder: ts.BuilderProgram | ts.EmitAndSemanticDiagnosticsBuilderProgram;
+ if (watch) {
+ builder = cache.oldBuilder =
+ ts.createEmitAndSemanticDiagnosticsBuilderProgram(
+ typeScriptProgram,
+ tsCompilerHost,
+ cache.oldBuilder
+ );
+ cache.oldNgtscProgram = angularProgram;
+ } else {
+ // When not in watch mode, the startup cost of the incremental analysis can be avoided by
+ // using an abstract builder that only wraps a TypeScript program.
+ builder = ts.createAbstractBuilder(typeScriptProgram, tsCompilerHost);
+ }
+
+ // Update semantic diagnostics cache
+ const affectedFiles = new Set();
+
+ // Analyze affected files when in watch mode for incremental type checking
+ if ('getSemanticDiagnosticsOfNextAffectedFile' in builder) {
+ // eslint-disable-next-line no-constant-condition
+ while (true) {
+ const result = builder.getSemanticDiagnosticsOfNextAffectedFile(
+ undefined,
+ (sourceFile) => {
+ // If the affected file is a TTC shim, add the shim's original source file.
+ // This ensures that changes that affect TTC are typechecked even when the changes
+ // are otherwise unrelated from a TS perspective and do not result in Ivy codegen changes.
+ // For example, changing @Input property types of a directive used in another component's
+ // template.
+ if (
+ ignoreForDiagnostics.has(sourceFile) &&
+ sourceFile.fileName.endsWith('.ngtypecheck.ts')
+ ) {
+ // This file name conversion relies on internal compiler logic and should be converted
+ // to an official method when available. 15 is length of `.ngtypecheck.ts`
+ const originalFilename = sourceFile.fileName.slice(0, -15) + '.ts';
+ const originalSourceFile = builder.getSourceFile(originalFilename);
+ if (originalSourceFile) {
+ affectedFiles.add(originalSourceFile);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+ );
+
+ if (!result) {
+ break;
+ }
+
+ affectedFiles.add(result.affected as ts.SourceFile);
+ }
+ }
+
+ // Collect program level diagnostics
+ const allDiagnostics: ts.Diagnostic[] = [
+ ...angularCompiler.getOptionDiagnostics(),
+ ...builder.getOptionsDiagnostics(),
+ ...builder.getGlobalDiagnostics(),
+ ];
+
+ // Required to support asynchronous resource loading
+ // Must be done before creating transformers or getting template diagnostics
+ await angularCompiler.analyzeAsync();
+
+ // Collect source file specific diagnostics
+ for (const sourceFile of builder.getSourceFiles()) {
+ if (!ignoreForDiagnostics.has(sourceFile)) {
+ allDiagnostics.push(
+ ...builder.getSyntacticDiagnostics(sourceFile),
+ ...builder.getSemanticDiagnostics(sourceFile)
+ );
+ }
+
+ if (sourceFile.isDeclarationFile) {
+ continue;
+ }
+
+ // Collect sources that are required to be emitted
+ if (
+ !ignoreForEmit.has(sourceFile) &&
+ !angularCompiler.incrementalDriver.safeToSkipEmit(sourceFile)
+ ) {
+ // If required to emit, diagnostics may have also changed
+ if (!ignoreForDiagnostics.has(sourceFile)) {
+ affectedFiles.add(sourceFile);
+ }
+ } else if (
+ sourceFileCache &&
+ !affectedFiles.has(sourceFile) &&
+ !ignoreForDiagnostics.has(sourceFile)
+ ) {
+ // Use cached Angular diagnostics for unchanged and unaffected files
+ const angularDiagnostics =
+ sourceFileCache.getAngularDiagnostics(sourceFile);
+ if (angularDiagnostics?.length) {
+ allDiagnostics.push(...angularDiagnostics);
+ }
+ }
+ }
+
+ // Collect new Angular diagnostics for files affected by changes
+ for (const affectedFile of affectedFiles) {
+ const angularDiagnostics = angularCompiler.getDiagnosticsForFile(
+ affectedFile,
+ /** OptimizeFor.WholeProgram */ 1
+ );
+
+ allDiagnostics.push(...angularDiagnostics);
+ sourceFileCache.updateAngularDiagnostics(affectedFile, angularDiagnostics);
+ }
+
+ const otherDiagnostics = [];
+ const errorDiagnostics = [];
+ for (const diagnostic of allDiagnostics) {
+ if (diagnostic.category === ts.DiagnosticCategory.Error) {
+ errorDiagnostics.push(diagnostic);
+ } else {
+ otherDiagnostics.push(diagnostic);
+ }
+ }
+
+ if (otherDiagnostics.length) {
+ log.msg(formatDiagnostics(errorDiagnostics));
+ }
+
+ if (errorDiagnostics.length) {
+ throw new Error(formatDiagnostics(errorDiagnostics));
+ }
+
+ const transformers = angularCompiler.prepareEmit().transformers;
+ for (const sourceFile of builder.getSourceFiles()) {
+ if (!ignoreForEmit.has(sourceFile)) {
+ builder.emit(sourceFile, undefined, undefined, undefined, transformers);
+ }
+ }
+}
diff --git a/packages/angular/src/executors/package/ng-packagr-adjustments/ts/cache-compiler-host.ts b/packages/angular/src/executors/package/ng-packagr-adjustments/ts/cache-compiler-host.ts
new file mode 100644
index 0000000000..8a2b6e6eaf
--- /dev/null
+++ b/packages/angular/src/executors/package/ng-packagr-adjustments/ts/cache-compiler-host.ts
@@ -0,0 +1,219 @@
+/**
+ * Adapted from the original ng-packagr source.
+ *
+ * Changes made:
+ * - Changed filePath passed to the StylesheetProcessor.parse when is a .ts file and inlineStyleLanguage is set.
+ */
+
+import type { CompilerHost, CompilerOptions } from '@angular/compiler-cli';
+import { createHash } from 'crypto';
+import { FileCache } from 'ng-packagr/lib/file-system/file-cache';
+import { BuildGraph } from 'ng-packagr/lib/graph/build-graph';
+import { Node } from 'ng-packagr/lib/graph/node';
+import { EntryPointNode, fileUrl } from 'ng-packagr/lib/ng-package/nodes';
+import { ensureUnixPath } from 'ng-packagr/lib/utils/path';
+import * as path from 'path';
+import * as ts from 'typescript';
+import {
+ InlineStyleLanguage,
+ StylesheetProcessor,
+} from '../styles/stylesheet-processor';
+
+export function cacheCompilerHost(
+ graph: BuildGraph,
+ entryPoint: EntryPointNode,
+ compilerOptions: CompilerOptions,
+ moduleResolutionCache: ts.ModuleResolutionCache,
+ stylesheetProcessor?: StylesheetProcessor,
+ inlineStyleLanguage?: InlineStyleLanguage,
+ sourcesFileCache: FileCache = entryPoint.cache.sourcesFileCache
+): CompilerHost {
+ const compilerHost = ts.createIncrementalCompilerHost(compilerOptions);
+
+ const getNode = (fileName: string) => {
+ const nodeUri = fileUrl(ensureUnixPath(fileName));
+ let node = graph.get(nodeUri);
+
+ if (!node) {
+ node = new Node(nodeUri);
+ graph.put(node);
+ }
+
+ return node;
+ };
+
+ const addDependee = (fileName: string) => {
+ const node = getNode(fileName);
+ entryPoint.dependsOn(node);
+ };
+
+ return {
+ ...compilerHost,
+
+ // ts specific
+ fileExists: (fileName: string) => {
+ const cache = sourcesFileCache.getOrCreate(fileName);
+ if (cache.exists === undefined) {
+ cache.exists = compilerHost.fileExists.call(this, fileName);
+ }
+
+ return cache.exists;
+ },
+
+ getSourceFile: (fileName: string, languageVersion: ts.ScriptTarget) => {
+ addDependee(fileName);
+ const cache = sourcesFileCache.getOrCreate(fileName);
+ if (!cache.sourceFile) {
+ cache.sourceFile = compilerHost.getSourceFile.call(
+ this,
+ fileName,
+ languageVersion
+ );
+ }
+
+ return cache.sourceFile;
+ },
+
+ writeFile: (
+ fileName: string,
+ data: string,
+ writeByteOrderMark: boolean,
+ onError?: (message: string) => void,
+ sourceFiles?: ReadonlyArray
+ ) => {
+ if (fileName.endsWith('.d.ts')) {
+ sourceFiles.forEach((source) => {
+ const cache = sourcesFileCache.getOrCreate(source.fileName);
+ if (!cache.declarationFileName) {
+ cache.declarationFileName = ensureUnixPath(fileName);
+ }
+ });
+ } else {
+ fileName = fileName.replace(/\.js(\.map)?$/, '.mjs$1');
+ const outputCache = entryPoint.cache.outputCache;
+
+ outputCache.set(fileName, {
+ content: data,
+ version: createHash('sha256').update(data).digest('hex'),
+ });
+ }
+
+ compilerHost.writeFile.call(
+ this,
+ fileName,
+ data,
+ writeByteOrderMark,
+ onError,
+ sourceFiles
+ );
+ },
+
+ readFile: (fileName: string) => {
+ addDependee(fileName);
+ const cache = sourcesFileCache.getOrCreate(fileName);
+ if (cache.content === undefined) {
+ cache.content = compilerHost.readFile.call(this, fileName);
+ }
+
+ return cache.content;
+ },
+
+ resolveModuleNames: (moduleNames: string[], containingFile: string) => {
+ return moduleNames.map((moduleName) => {
+ const { resolvedModule } = ts.resolveModuleName(
+ moduleName,
+ ensureUnixPath(containingFile),
+ compilerOptions,
+ compilerHost,
+ moduleResolutionCache
+ );
+
+ return resolvedModule;
+ });
+ },
+
+ resourceNameToFileName: (
+ resourceName: string,
+ containingFilePath: string
+ ) => {
+ const resourcePath = path.resolve(
+ path.dirname(containingFilePath),
+ resourceName
+ );
+ const containingNode = getNode(containingFilePath);
+ const resourceNode = getNode(resourcePath);
+ containingNode.dependsOn(resourceNode);
+
+ return resourcePath;
+ },
+
+ readResource: async (fileName: string) => {
+ addDependee(fileName);
+
+ const cache = sourcesFileCache.getOrCreate(fileName);
+ if (cache.content === undefined) {
+ if (/(?:html?|svg)$/.test(path.extname(fileName))) {
+ // template
+ cache.content = compilerHost.readFile.call(this, fileName);
+ } else {
+ // stylesheet
+ cache.content = await stylesheetProcessor.process({
+ filePath: fileName,
+ content: compilerHost.readFile.call(this, fileName),
+ });
+ }
+
+ if (cache.content === undefined) {
+ throw new Error(`Cannot read file ${fileName}.`);
+ }
+
+ cache.exists = true;
+ }
+
+ return cache.content;
+ },
+ transformResource: async (data, context) => {
+ if (context.resourceFile || context.type !== 'style') {
+ return null;
+ }
+
+ if (inlineStyleLanguage) {
+ const key = createHash('sha1').update(data).digest('hex');
+ const fileName = `${context.containingFile}-${key}.${inlineStyleLanguage}`;
+ const cache = sourcesFileCache.getOrCreate(fileName);
+ if (cache.content === undefined) {
+ cache.content = await stylesheetProcessor.process({
+ filePath: context.containingFile, // @leosvelperez: changed from fileName
+ content: data,
+ });
+
+ const virtualFileNode = getNode(fileName);
+ const containingFileNode = getNode(context.containingFile);
+ virtualFileNode.dependsOn(containingFileNode);
+ }
+
+ cache.exists = true;
+
+ return { content: cache.content };
+ }
+
+ return null;
+ },
+ };
+}
+
+export function augmentProgramWithVersioning(program: ts.Program): void {
+ const baseGetSourceFiles = program.getSourceFiles;
+ program.getSourceFiles = function (...parameters) {
+ const files: readonly (ts.SourceFile & { version?: string })[] =
+ baseGetSourceFiles(...parameters);
+
+ for (const file of files) {
+ if (file.version === undefined) {
+ file.version = createHash('sha256').update(file.text).digest('hex');
+ }
+ }
+
+ return files;
+ };
+}