Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Angular: Support v19.2 when @angular/animations is not installed #30611

Merged
merged 2 commits into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion code/builders/builder-webpack5/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ import prettyTime from 'pretty-hrtime';
import sirv from 'sirv';
import { corePath } from 'storybook/core-path';
import type { Configuration, Stats, StatsOptions } from 'webpack';
import webpackDep, { DefinePlugin, ProgressPlugin } from 'webpack';
import webpackDep, { DefinePlugin, IgnorePlugin, ProgressPlugin } from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';

export * from './types';
export * from './preview/virtual-module-mapping';

export const WebpackDefinePlugin = DefinePlugin;
export const WebpackIgnorePlugin = IgnorePlugin;

export const printDuration = (startTime: [number, number]) =>
prettyTime(process.hrtime(startTime))
Expand Down
4 changes: 4 additions & 0 deletions code/frameworks/angular/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
"@angular-devkit/architect": ">=0.1500.0 < 0.2000.0",
"@angular-devkit/build-angular": ">=15.0.0 < 20.0.0",
"@angular-devkit/core": ">=15.0.0 < 20.0.0",
"@angular/animations": ">=15.0.0 < 20.0.0",
"@angular/cli": ">=15.0.0 < 20.0.0",
"@angular/common": ">=15.0.0 < 20.0.0",
"@angular/compiler": ">=15.0.0 < 20.0.0",
Expand All @@ -115,6 +116,9 @@
"zone.js": ">= 0.11.1 < 1.0.0"
},
"peerDependenciesMeta": {
"@angular/animations": {
"optional": true
},
"@angular/cli": {
"optional": true
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export abstract class AbstractRenderer {
this.initAngularRootElement(targetDOMNode, targetSelector);

const analyzedMetadata = new PropertyExtractor(storyFnAngular.moduleMetadata, component);
await analyzedMetadata.init();

const storyUid = this.generateStoryUIdFromRawStoryUid(
targetDOMNode.getAttribute(STORY_UID_ATTRIBUTE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ describe('StorybookModule', () => {
};

const analyzedMetadata = new PropertyExtractor({}, FooComponent);
await analyzedMetadata.init();

const application = getApplication({
storyFnAngular: { props },
Expand Down Expand Up @@ -98,6 +99,7 @@ describe('StorybookModule', () => {
};

const analyzedMetadata = new PropertyExtractor({}, FooComponent);
await analyzedMetadata.init();

const application = getApplication({
storyFnAngular: { props },
Expand Down Expand Up @@ -127,6 +129,7 @@ describe('StorybookModule', () => {
const storyProps$ = new BehaviorSubject<ICollection>(initialProps);

const analyzedMetadata = new PropertyExtractor({}, FooComponent);
await analyzedMetadata.init();

const application = getApplication({
storyFnAngular: { props: initialProps },
Expand Down Expand Up @@ -183,6 +186,7 @@ describe('StorybookModule', () => {
const storyProps$ = new BehaviorSubject<ICollection>(initialProps);

const analyzedMetadata = new PropertyExtractor({}, FooComponent);
await analyzedMetadata.init();

const application = getApplication({
storyFnAngular: { props: initialProps },
Expand Down Expand Up @@ -224,6 +228,7 @@ describe('StorybookModule', () => {
const storyProps$ = new BehaviorSubject<ICollection>(initialProps);

const analyzedMetadata = new PropertyExtractor({}, FooComponent);
await analyzedMetadata.init();

const application = getApplication({
storyFnAngular: {
Expand Down Expand Up @@ -262,6 +267,7 @@ describe('StorybookModule', () => {
const storyProps$ = new BehaviorSubject<ICollection>(initialProps);

const analyzedMetadata = new PropertyExtractor({}, FooComponent);
await analyzedMetadata.init();

const application = getApplication({
storyFnAngular: { props: initialProps },
Expand Down Expand Up @@ -302,6 +308,8 @@ describe('StorybookModule', () => {
WithoutSelectorComponent
);

await analyzedMetadata.init();

const application = getApplication({
storyFnAngular: {
props,
Expand Down Expand Up @@ -330,6 +338,7 @@ describe('StorybookModule', () => {
class FooComponent {}

const analyzedMetadata = new PropertyExtractor({}, FooComponent);
await analyzedMetadata.init();

const application = getApplication({
storyFnAngular: { template: '' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,89 +29,95 @@ const TestModuleWithImportsAndProviders = NgModule({
providers: [TestTokenProvider],
})(class {});

const analyzeMetadata = (metadata: NgModuleMetadata, component?: any) => {
return new PropertyExtractor(metadata, component);
const analyzeMetadata = async (metadata: NgModuleMetadata, component?: any) => {
const propertyExtractor = new PropertyExtractor(metadata, component);
await propertyExtractor.init();
return propertyExtractor;
};
const extractImports = (metadata: NgModuleMetadata, component?: any) => {
const { imports } = new PropertyExtractor(metadata, component);
return imports;
const extractImports = async (metadata: NgModuleMetadata, component?: any) => {
const propertyExtractor = new PropertyExtractor(metadata, component);
await propertyExtractor.init();
return propertyExtractor.imports;
};
const extractDeclarations = (metadata: NgModuleMetadata, component?: any) => {
const { declarations } = new PropertyExtractor(metadata, component);
return declarations;
const extractDeclarations = async (metadata: NgModuleMetadata, component?: any) => {
const propertyExtractor = new PropertyExtractor(metadata, component);
await propertyExtractor.init();
return propertyExtractor.declarations;
};
const extractProviders = (metadata: NgModuleMetadata, component?: any) => {
const { providers } = new PropertyExtractor(metadata, component);
return providers;
const extractProviders = async (metadata: NgModuleMetadata, component?: any) => {
const propertyExtractor = new PropertyExtractor(metadata, component);
await propertyExtractor.init();
return propertyExtractor.providers;
};
const extractApplicationProviders = (metadata: NgModuleMetadata, component?: any) => {
const { applicationProviders } = new PropertyExtractor(metadata, component);
return applicationProviders;
const extractApplicationProviders = async (metadata: NgModuleMetadata, component?: any) => {
const propertyExtractor = new PropertyExtractor(metadata, component);
await propertyExtractor.init();
return propertyExtractor.applicationProviders;
};

describe('PropertyExtractor', () => {
vi.spyOn(console, 'warn').mockImplementation(() => {});

describe('analyzeMetadata', () => {
it('should remove BrowserModule', () => {
it('should remove BrowserModule', async () => {
const metadata = {
imports: [BrowserModule],
};
const { imports, providers, applicationProviders } = analyzeMetadata(metadata);
const { imports, providers, applicationProviders } = await analyzeMetadata(metadata);
expect(imports.flat(Number.MAX_VALUE)).toEqual([CommonModule]);
expect(providers.flat(Number.MAX_VALUE)).toEqual([]);
expect(applicationProviders.flat(Number.MAX_VALUE)).toEqual([]);
});

it('should remove BrowserAnimationsModule and use its providers instead', () => {
it('should remove BrowserAnimationsModule and use its providers instead', async () => {
const metadata = {
imports: [BrowserAnimationsModule],
};
const { imports, providers, applicationProviders } = analyzeMetadata(metadata);
const { imports, providers, applicationProviders } = await analyzeMetadata(metadata);
expect(imports.flat(Number.MAX_VALUE)).toEqual([CommonModule]);
expect(providers.flat(Number.MAX_VALUE)).toEqual([]);
expect(applicationProviders.flat(Number.MAX_VALUE)).toEqual(provideAnimations());
});

it('should remove NoopAnimationsModule and use its providers instead', () => {
it('should remove NoopAnimationsModule and use its providers instead', async () => {
const metadata = {
imports: [NoopAnimationsModule],
};
const { imports, providers, applicationProviders } = analyzeMetadata(metadata);
const { imports, providers, applicationProviders } = await analyzeMetadata(metadata);
expect(imports.flat(Number.MAX_VALUE)).toEqual([CommonModule]);
expect(providers.flat(Number.MAX_VALUE)).toEqual([]);
expect(applicationProviders.flat(Number.MAX_VALUE)).toEqual(provideNoopAnimations());
});

it('should remove Browser/Animations modules recursively', () => {
it('should remove Browser/Animations modules recursively', async () => {
const metadata = {
imports: [BrowserAnimationsModule, BrowserModule],
};
const { imports, providers, applicationProviders } = analyzeMetadata(metadata);
const { imports, providers, applicationProviders } = await analyzeMetadata(metadata);
expect(imports.flat(Number.MAX_VALUE)).toEqual([CommonModule]);
expect(providers.flat(Number.MAX_VALUE)).toEqual([]);
expect(applicationProviders.flat(Number.MAX_VALUE)).toEqual(provideAnimations());
});

it('should not destructure Angular official module', () => {
it('should not destructure Angular official module', async () => {
const metadata = {
imports: [WithOfficialModule],
};
const { imports, providers, applicationProviders } = analyzeMetadata(metadata);
const { imports, providers, applicationProviders } = await analyzeMetadata(metadata);
expect(imports.flat(Number.MAX_VALUE)).toEqual([CommonModule, WithOfficialModule]);
expect(providers.flat(Number.MAX_VALUE)).toEqual([]);
expect(applicationProviders.flat(Number.MAX_VALUE)).toEqual([]);
});
});

describe('extractImports', () => {
it('should return Angular official modules', () => {
const imports = extractImports({ imports: [TestModuleWithImportsAndProviders] });
it('should return Angular official modules', async () => {
const imports = await extractImports({ imports: [TestModuleWithImportsAndProviders] });
expect(imports).toEqual([CommonModule, TestModuleWithImportsAndProviders]);
});

it('should return standalone components', () => {
const imports = extractImports(
it('should return standalone components', async () => {
const imports = await extractImports(
{
imports: [TestModuleWithImportsAndProviders],
},
Expand All @@ -124,8 +130,8 @@ describe('PropertyExtractor', () => {
]);
});

it('should return standalone directives', () => {
const imports = extractImports(
it('should return standalone directives', async () => {
const imports = await extractImports(
{
imports: [TestModuleWithImportsAndProviders],
},
Expand All @@ -140,8 +146,11 @@ describe('PropertyExtractor', () => {
});

describe('extractDeclarations', () => {
it('should return an array of declarations that contains `storyComponent`', () => {
const declarations = extractDeclarations({ declarations: [TestComponent1] }, TestComponent2);
it('should return an array of declarations that contains `storyComponent`', async () => {
const declarations = await extractDeclarations(
{ declarations: [TestComponent1] },
TestComponent2
);
expect(declarations).toEqual([TestComponent1, TestComponent2]);
});
});
Expand Down Expand Up @@ -174,15 +183,15 @@ describe('PropertyExtractor', () => {
});

describe('extractProviders', () => {
it('should return an array of providers', () => {
const providers = extractProviders({
it('should return an array of providers', async () => {
const providers = await extractProviders({
providers: [TestService],
});
expect(providers).toEqual([TestService]);
});

it('should return an array of singletons extracted', () => {
const singeltons = extractApplicationProviders({
it('should return an array of singletons extracted', async () => {
const singeltons = await extractApplicationProviders({
imports: [BrowserAnimationsModule],
});

Expand Down
Loading
Loading