diff --git a/code/frameworks/angular/package.json b/code/frameworks/angular/package.json index 322ae47e028f..ecacc76ba73a 100644 --- a/code/frameworks/angular/package.json +++ b/code/frameworks/angular/package.json @@ -102,7 +102,6 @@ "@angular/platform-browser": ">=13.0.0", "@angular/platform-browser-dynamic": ">=13.0.0", "@babel/core": "*", - "@nrwl/workspace": "14.6.1", "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", "rxjs": "^6.0.0 || ^7.4.0", @@ -112,9 +111,6 @@ "peerDependenciesMeta": { "@angular/cli": { "optional": true - }, - "@nrwl/workspace": { - "optional": true } }, "engines": { diff --git a/code/frameworks/angular/src/client/angular-beta/utils/NgComponentAnalyzer.ts b/code/frameworks/angular/src/client/angular-beta/utils/NgComponentAnalyzer.ts index 2e5497ff8130..4d807b4d59ca 100644 --- a/code/frameworks/angular/src/client/angular-beta/utils/NgComponentAnalyzer.ts +++ b/code/frameworks/angular/src/client/angular-beta/utils/NgComponentAnalyzer.ts @@ -114,9 +114,7 @@ export const isStandaloneComponent = (component: any): component is Type d instanceof Component && (d as any).standalone); + return (decorators || []).some((d) => d instanceof Component && d.standalone); }; /** diff --git a/code/frameworks/angular/src/server/angular-read-workspace.ts b/code/frameworks/angular/src/server/angular-read-workspace.ts deleted file mode 100644 index e22ceb99f2f0..000000000000 --- a/code/frameworks/angular/src/server/angular-read-workspace.ts +++ /dev/null @@ -1,94 +0,0 @@ -/* eslint-disable global-require */ -import { NodeJsSyncHost } from '@angular-devkit/core/node'; -import { workspaces } from '@angular-devkit/core'; - -/** - * Returns the workspace definition - * - * - Either from NX if it is present - * - Either from `@angular-devkit/core` -> https://github.com/angular/angular-cli/tree/master/packages/angular_devkit/core - */ -export const readAngularWorkspaceConfig = async ( - dirToSearch: string -): Promise => { - const host = workspaces.createWorkspaceHost(new NodeJsSyncHost()); - - try { - /** - * Apologies for the following line - * If there's a better way to do it, let's do it - */ - - // catch if nx.json does not exist - require('@nrwl/workspace').readNxJson(); - - const nxWorkspace = require('@nrwl/workspace').readWorkspaceConfig({ - format: 'angularCli', - path: dirToSearch, - }); - - // Use the workspace version of nx when angular looks for the angular.json file - host.readFile = (path) => { - if (typeof path === 'string' && path.endsWith('angular.json')) { - return Promise.resolve(JSON.stringify(nxWorkspace)); - } - return host.readFile(path); - }; - host.isFile = (path) => { - if (typeof path === 'string' && path.endsWith('angular.json')) { - return Promise.resolve(true); - } - return host.isFile(path); - }; - } catch (e) { - // Ignore if the client does not use NX - } - - return (await workspaces.readWorkspace(dirToSearch, host)).workspace; -}; - -export const getDefaultProjectName = (workspace: workspaces.WorkspaceDefinition): string => { - const environmentProjectName = process.env.STORYBOOK_ANGULAR_PROJECT; - if (environmentProjectName) { - return environmentProjectName; - } - - if (workspace.projects.has('storybook')) { - return 'storybook'; - } - if (workspace.extensions.defaultProject) { - return workspace.extensions.defaultProject as string; - } - - const firstProjectName = workspace.projects.keys().next().value; - if (firstProjectName) { - return firstProjectName; - } - throw new Error('No angular projects found'); -}; - -export const findAngularProjectTarget = ( - workspace: workspaces.WorkspaceDefinition, - projectName: string, - targetName: string -): { - project: workspaces.ProjectDefinition; - target: workspaces.TargetDefinition; -} => { - if (!workspace.projects || !Object.keys(workspace.projects).length) { - throw new Error('No angular projects found'); - } - - const project = workspace.projects.get(projectName); - - if (!project) { - throw new Error(`"${projectName}" project is not found in angular.json`); - } - - if (!project.targets.has(targetName)) { - throw new Error(`"${targetName}" target is not found in "${projectName}" project`); - } - const target = project.targets.get(targetName); - - return { project, target }; -}; diff --git a/code/frameworks/angular/src/server/framework-preset-angular-cli.ts b/code/frameworks/angular/src/server/framework-preset-angular-cli.ts index 0af9a9217bf6..f1e25fd5971a 100644 --- a/code/frameworks/angular/src/server/framework-preset-angular-cli.ts +++ b/code/frameworks/angular/src/server/framework-preset-angular-cli.ts @@ -1,19 +1,12 @@ import webpack from 'webpack'; import { logger } from '@storybook/node-logger'; -import { BuilderContext, Target, targetFromTargetString } from '@angular-devkit/architect'; +import { BuilderContext, targetFromTargetString } from '@angular-devkit/architect'; import { sync as findUpSync } from 'find-up'; -import semver from 'semver'; -import { dedent } from 'ts-dedent'; import { JsonObject, logging } from '@angular-devkit/core'; import { getWebpackConfig as getCustomWebpackConfig } from './angular-cli-webpack'; import { moduleIsAvailable } from './utils/module-is-available'; import { PresetOptions } from './preset-options'; -import { - getDefaultProjectName, - findAngularProjectTarget, - readAngularWorkspaceConfig, -} from './angular-read-workspace'; export async function webpackFinal(baseConfig: webpack.Configuration, options: PresetOptions) { if (!moduleIsAvailable('@angular-devkit/build-angular')) { @@ -21,43 +14,16 @@ export async function webpackFinal(baseConfig: webpack.Configuration, options: P return baseConfig; } - const angularCliVersion = await import('@angular/cli').then((m) => semver.coerce(m.VERSION.full)); + const builderContext = getBuilderContext(options); + const builderOptions = await getBuilderOptions(options, builderContext); - /** - * Ordered array to use the specific getWebpackConfig according to some condition like angular-cli version - */ - const webpackGetterByVersions: { - info: string; - condition: boolean; - getWebpackConfig( - baseConfig: webpack.Configuration, - options: PresetOptions - ): Promise | webpack.Configuration; - }[] = [ - { - info: '=> Loading angular-cli config for angular >= 13.0.0', - condition: semver.satisfies(angularCliVersion, '>=13.0.0'), - getWebpackConfig: async (_baseConfig, _options) => { - const builderContext = getBuilderContext(_options); - const builderOptions = await getBuilderOptions(_options, builderContext); - const legacyDefaultOptions = await getLegacyDefaultBuildOptions(_options); - - return getCustomWebpackConfig(_baseConfig, { - builderOptions: { - watch: options.configType === 'DEVELOPMENT', - ...legacyDefaultOptions, - ...builderOptions, - }, - builderContext, - }); - }, + return getCustomWebpackConfig(baseConfig, { + builderOptions: { + watch: options.configType === 'DEVELOPMENT', + ...builderOptions, }, - ]; - - const webpackGetter = webpackGetterByVersions.find((wg) => wg.condition); - - logger.info(webpackGetter.info); - return Promise.resolve(webpackGetter.getWebpackConfig(baseConfig, options)); + builderContext, + }); } /** @@ -115,59 +81,3 @@ async function getBuilderOptions( return builderOptions; } - -/** - * Get options from legacy way - * /!\ This is only for backward compatibility and would be removed on Storybook 7.0 - * only work for angular.json with [defaultProject].build or "storybook.build" config - */ -async function getLegacyDefaultBuildOptions(options: PresetOptions) { - if (options.angularBrowserTarget !== undefined) { - // Not use legacy way with builder (`angularBrowserTarget` is defined or null with builder and undefined without) - return {}; - } - - logger.warn(dedent`Your Storybook startup uses a solution that will not be supported in version 7.0. - You must use angular builder to have an explicit configuration on the project used in angular.json - Read more at: - - https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#sb-angular-builder) - - https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#angular13) - `); - const dirToSearch = process.cwd(); - - // Read angular workspace - let workspaceConfig; - try { - workspaceConfig = await readAngularWorkspaceConfig(dirToSearch); - } catch (error) { - logger.error( - `=> Could not find angular workspace config (angular.json) on this path "${dirToSearch}"` - ); - logger.info(`=> Fail to load angular-cli config. Using base config`); - return {}; - } - - // Find angular project target - try { - const browserTarget = { - configuration: undefined, - project: getDefaultProjectName(workspaceConfig), - target: 'build', - } as Target; - - const { target } = findAngularProjectTarget( - workspaceConfig, - browserTarget.project, - browserTarget.target - ); - - logger.info( - `=> Using angular project "${browserTarget.project}:${browserTarget.target}" for configuring Storybook` - ); - return { ...target.options }; - } catch (error) { - logger.error(`=> Could not find angular project: ${error.message}`); - logger.info(`=> Fail to load angular-cli config. Using base config`); - return {}; - } -} diff --git a/code/frameworks/angular/src/server/utils/normalize-asset-patterns.ts b/code/frameworks/angular/src/server/utils/normalize-asset-patterns.ts deleted file mode 100644 index 4d8d7279605b..000000000000 --- a/code/frameworks/angular/src/server/utils/normalize-asset-patterns.ts +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Clone of `normalizeAssetPatterns` function from angular-cli v11.2.* - * > https://github.com/angular/angular-cli/blob/de63f41d669e42ada84f94ca1795d2791b9b45cc/packages/angular_devkit/build_angular/src/utils/normalize-asset-patterns.ts - * - * It is not possible to use the original because arguments have changed between version 6.1.* and 11.*.* of angular-cli - */ -import { statSync } from 'fs'; -import { - Path, - BaseException, - basename, - dirname, - getSystemPath, - join, - normalize, - relative, - resolve, -} from '@angular-devkit/core'; -import { AssetPattern } from '@angular-devkit/build-angular'; -import { AssetPatternClass } from '@angular-devkit/build-angular/src/builders/browser/schema'; - -export class MissingAssetSourceRootException extends BaseException { - constructor(path: string) { - super(`The ${path} asset path must start with the project source root.`); - } -} - -export function normalizeAssetPatterns( - assetPatterns: AssetPattern[], - root: Path, - projectRoot: Path, - maybeSourceRoot: Path | undefined -): AssetPatternClass[] { - // When sourceRoot is not available, we default to ${projectRoot}/src. - const sourceRoot = maybeSourceRoot || join(projectRoot, 'src'); - const resolvedSourceRoot = resolve(root, sourceRoot); - - if (assetPatterns.length === 0) { - return []; - } - - return assetPatterns.map((assetPattern) => { - // Normalize string asset patterns to objects. - if (typeof assetPattern === 'string') { - const assetPath = normalize(assetPattern); - const resolvedAssetPath = resolve(root, assetPath); - - // Check if the string asset is within sourceRoot. - if (!resolvedAssetPath.startsWith(resolvedSourceRoot)) { - throw new MissingAssetSourceRootException(assetPattern); - } - - let glob: string; - let input: Path; - let isDirectory = false; - - try { - isDirectory = statSync(getSystemPath(resolvedAssetPath)).isDirectory(); - } catch { - isDirectory = true; - } - - if (isDirectory) { - // Folders get a recursive star glob. - glob = '**/*'; - // Input directory is their original path. - input = assetPath; - } else { - // Files are their own glob. - glob = basename(assetPath); - // Input directory is their original dirname. - input = dirname(assetPath); - } - - // Output directory for both is the relative path from source root to input. - const output = relative(resolvedSourceRoot, resolve(root, input)); - - // Return the asset pattern in object format. - return { glob, input, output }; - } - // It's already an AssetPatternObject, no need to convert. - return assetPattern; - }); -} diff --git a/code/frameworks/angular/src/server/utils/normalize-optimization.ts b/code/frameworks/angular/src/server/utils/normalize-optimization.ts deleted file mode 100644 index 345f3919e056..000000000000 --- a/code/frameworks/angular/src/server/utils/normalize-optimization.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { OptimizationUnion } from '@angular-devkit/build-angular'; -import { NormalizedOptimizationOptions } from '@angular-devkit/build-angular/src/utils/normalize-optimization'; -import { moduleIsAvailable } from './module-is-available'; - -const importAngularCliNormalizeOptimization = (): - | typeof import('@angular-devkit/build-angular/src/utils/normalize-optimization') - | undefined => { - // First we look for webpack config according to directory structure of Angular - // present since the version 7.2.0 - if (moduleIsAvailable('@angular-devkit/build-angular/src/utils/normalize-optimization')) { - // eslint-disable-next-line global-require - return require('@angular-devkit/build-angular/src/utils/normalize-optimization'); - } - return undefined; -}; - -export const normalizeOptimization = ( - options: OptimizationUnion -): NormalizedOptimizationOptions => { - if (importAngularCliNormalizeOptimization()) { - return importAngularCliNormalizeOptimization().normalizeOptimization(options); - } - - // Best effort to stay compatible with 6.1.* - return options as any; -}; diff --git a/code/lib/cli/src/generators/ANGULAR/angular-helpers.ts b/code/lib/cli/src/generators/ANGULAR/angular-helpers.ts deleted file mode 100644 index a1ab6d8f9221..000000000000 --- a/code/lib/cli/src/generators/ANGULAR/angular-helpers.ts +++ /dev/null @@ -1,84 +0,0 @@ -import * as path from 'path'; -import * as fs from 'fs'; -import { pathExists } from 'fs-extra'; - -import { readFileAsJson, writeFileAsJson } from '../../helpers'; - -type TsConfig = { - extends: string; -}; - -export function getAngularAppTsConfigPath() { - const angularJson = readFileAsJson('angular.json', true); - const defaultProject = getDefaultProjectName(angularJson); - const tsConfigPath = angularJson.projects[defaultProject].architect.build.options.tsConfig; - - if (!tsConfigPath || !fs.existsSync(path.resolve(tsConfigPath))) { - return false; - } - return tsConfigPath; -} - -export function getAngularAppTsConfigJson() { - const tsConfigPath = getAngularAppTsConfigPath(); - - if (!tsConfigPath) { - return false; - } - - return readFileAsJson(tsConfigPath, true); -} - -function setStorybookTsconfigExtendsPath(tsconfigJson: TsConfig) { - const angularProjectTsConfigPath = getAngularAppTsConfigPath(); - const newTsconfigJson = { ...tsconfigJson }; - newTsconfigJson.extends = `../${angularProjectTsConfigPath}`; - return newTsconfigJson; -} - -export function editStorybookTsConfig(tsconfigPath: string) { - let tsConfigJson; - try { - tsConfigJson = readFileAsJson(tsconfigPath); - } catch (e) { - if (e.name === 'SyntaxError' && e.message.indexOf('Unexpected token /') > -1) { - throw new Error(`Comments are disallowed in ${tsconfigPath}`); - } - throw e; - } - tsConfigJson = setStorybookTsconfigExtendsPath(tsConfigJson); - writeFileAsJson(tsconfigPath, tsConfigJson); -} - -export function getDefaultProjectName(angularJson: any): string | undefined { - const { defaultProject, projects } = angularJson; - - if (projects?.storybook) { - return 'storybook'; - } - - if (defaultProject) { - return defaultProject; - } - - const firstProjectName = projects ? Object.keys(projects)[0] : undefined; - if (firstProjectName) { - return firstProjectName; - } - - return undefined; -} - -export function checkForProjects() { - const { projects } = readFileAsJson('angular.json', true); - - if (!projects || Object.keys(projects).length === 0) { - throw new Error( - 'Could not find a project in your Angular workspace. \nAdd a project and re-run the installation' - ); - } -} - -export async function getBaseTsConfigName() { - return (await pathExists('./tsconfig.base.json')) ? 'tsconfig.base.json' : 'tsconfig.json'; -}