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

Serve styles and assets using .angular-cli webpack configuration #2735

Merged
merged 17 commits into from
Jan 19, 2018
Merged
Show file tree
Hide file tree
Changes from 8 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
87 changes: 87 additions & 0 deletions app/angular/src/server/angular-cli_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
const path = require('path');
const fs = require('fs');

const logger = console;

export function isAngularCliInstalled() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why exporting it?

try {
require.resolve('@angular/cli');
return true;
} catch (e) {
return false;
}
}

export function getAngularCliWebpackConfigOptions(dirToSearch, appIndex = 0) {
const fname = path.join(dirToSearch, '.angular-cli.json');
if (!fs.existsSync(fname)) {
return null;
}
const cliConfig = JSON.parse(fs.readFileSync(fname, 'utf8'));
if (!cliConfig.apps || !cliConfig.apps.length) {
throw new Error('.angular-cli.json must have apps entry.');
}
const appConfig = cliConfig.apps[appIndex];

const cliWebpackConfigOptions = {
projectRoot: dirToSearch,
appConfig,
buildOptions: {
outputPath: 'outputPath', // It's dummy value to avoid to Angular CLI's error
},
supportES2015: false,
};

return cliWebpackConfigOptions;
}

export function applyAngularCliWebpackConfig(baseConfig, cliWebpackConfigOptions) {
if (!cliWebpackConfigOptions) return baseConfig;

if (!isAngularCliInstalled()) {
logger.info('=> Using base config because @angular/cli is not installed.');
return baseConfig;
}

// eslint-disable-next-line global-require, import/no-extraneous-dependencies
const ngcliConfigFactory = require('@angular/cli/models/webpack-configs');

let cliCommonConfig;
let cliStyleConfig;
try {
cliCommonConfig = ngcliConfigFactory.getCommonConfig(cliWebpackConfigOptions);
cliStyleConfig = ngcliConfigFactory.getStylesConfig(cliWebpackConfigOptions);
} catch (e) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see this changed a bit.

I'd like to extract a function that detects the installment of @angular/cli.

If it's not installed, we just merge the base, we might log that.
If it's installed we try to get their webpack config.
This might fail, since it's a deep file in a npm module.
We should log in either case.

logger.warn('=> Failed to get angular-cli webpack config.');
return baseConfig;
}
logger.info('=> Get angular-cli webpack config.');

// Don't use storybooks .css/.scss rules because we have to use rules created by @angualr/cli
// because @angular/cli created rules have include/exclude for global style files.
const styleRules = baseConfig.module.rules.filter(
rule =>
!rule.test || (rule.test.toString() !== '/\\.css$/' && rule.test.toString() !== '/\\.scss$/')
);

// cliStyleConfig.entry adds global style files to the webpack context
const entry = {
...baseConfig.entry,
...cliStyleConfig.entry,
};

const mod = {
...baseConfig.module,
rules: [...cliStyleConfig.module.rules, ...styleRules],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we actually need these styleRules if they should be filtered out?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, the variable name styleRules is so bad 🙇
This array includes rules for .jsx, .ts, ..., so we need it.

I'll change the variable's name.

};

// We use cliCommonConfig plugins to serve static assets files.
const plugins = [...cliStyleConfig.plugins, ...cliCommonConfig.plugins, ...baseConfig.plugins];

return {
...baseConfig,
entry,
module: mod,
plugins,
};
}
15 changes: 13 additions & 2 deletions app/angular/src/server/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import fs from 'fs';
import path from 'path';
import loadBabelConfig from './babel_config';
import loadTsConfig from './ts_config';
import {
getAngularCliWebpackConfigOptions,
applyAngularCliWebpackConfig,
} from './angular-cli_config';

// avoid ESLint errors
const logger = console;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use @storybook/node-logger

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Quramy, I think this is the last comment to take care of

Expand Down Expand Up @@ -39,6 +43,12 @@ export default function(configType, baseConfig, configDir) {
config.entry.manager.unshift(storybookDefaultAddonsPath);
}

// Check whether project has Angular CLI configuration file
const cliWebpackConfigOptions = getAngularCliWebpackConfigOptions(process.cwd());
if (cliWebpackConfigOptions) {
logger.info('=> Loading angular-cli config.');
}

// Check whether user has a custom webpack config file and
// return the (extended) base configuration if it's not available.
const customConfigPath = path.resolve(configDir, 'webpack.config.js');
Expand All @@ -48,15 +58,16 @@ export default function(configType, baseConfig, configDir) {
const configPath = path.resolve(__dirname, './config/defaults/webpack.config.js');
const customConfig = require(configPath);

return customConfig(config);
return applyAngularCliWebpackConfig(customConfig(config), cliWebpackConfigOptions);
}
const customConfig = require(customConfigPath);

if (typeof customConfig === 'function') {
logger.info('=> Loading custom webpack config (full-control mode).');
return customConfig(config, configType);
return customConfig(applyAngularCliWebpackConfig(config, cliWebpackConfigOptions), configType);
}
logger.info('=> Loading custom webpack config (extending mode).');

return {
...customConfig,
// We'll always load our configurations after the custom config.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.red-color {
color: red;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div>
<p class="red-color">Styled with scoped CSS</p>
<p class="blue-color">Styled with scoped SCSS</p>
<p class="green-color">Styled with global CSS</p>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
div {
p.blue-color {
color: blue;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { storiesOf } from '@storybook/angular';
import { StyledComponent } from './styled.component';

storiesOf('Component styles', module).add('Component with styles', () => ({
component: StyledComponent,
}));
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Component } from '@angular/core';

@Component({
selector: 'storybook-styled-component',
templateUrl: './styled.component.html',
styleUrls: ['./styled.component.css', './styled.component.scss'],
})
export class StyledComponent {}
4 changes: 4 additions & 0 deletions examples/angular-cli/src/styles.css
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
/* You can add global styles to this file, and also import other style files */

.green-color {
color: green;
}