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 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
1 change: 1 addition & 0 deletions app/angular/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@storybook/addons": "^3.4.0-alpha.4",
"@storybook/channel-postmessage": "^3.4.0-alpha.4",
"@storybook/core": "^3.4.0-alpha.4",
"@storybook/node-logger": "^3.4.0-alpha.4",
"@storybook/ui": "^3.4.0-alpha.4",
"airbnb-js-shims": "^1.1.1",
"angular2-template-loader": "^0.6.2",
Expand Down
86 changes: 86 additions & 0 deletions app/angular/src/server/angular-cli_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import path from 'path';
import fs from 'fs';
import { logger } from '@storybook/node-logger';

function isAngularCliInstalled() {
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) {
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 rulesExcludingStyles = 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, ...rulesExcludingStyles],
};

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

return {
...baseConfig,
entry,
module: mod,
plugins,
};
}
4 changes: 1 addition & 3 deletions app/angular/src/server/babel_config.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import fs from 'fs';
import path from 'path';
import JSON5 from 'json5';
import { logger } from '@storybook/node-logger';
import defaultConfig from './config/babel';

// avoid ESLint errors
const logger = console;

function removeReactHmre(presets) {
const index = presets.indexOf('react-hmre');
if (index > -1) {
Expand Down
8 changes: 3 additions & 5 deletions app/angular/src/server/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@ import path from 'path';
import fs from 'fs';
import chalk from 'chalk';
import shelljs from 'shelljs';
import { logger } from '@storybook/node-logger';
import packageJson from '../../package.json';
import getBaseConfig from './config/webpack.config.prod';
import loadConfig from './config';
import { parseList, getEnvConfig } from './utils';

process.env.NODE_ENV = process.env.NODE_ENV || 'production';

// avoid ESLint errors
const logger = console;

program
.version(packageJson.version)
.option('-s, --static-dir <dir-names>', 'Directory where to load static files from', parseList)
Expand Down Expand Up @@ -66,13 +64,13 @@ if (program.staticDir) {
logger.error(`Error: no such directory to load static files: ${dir}`);
process.exit(-1);
}
logger.log(`=> Copying static files from: ${dir}`);
logger.info(`=> Copying static files from: ${dir}`);
shelljs.cp('-r', `${dir}/*`, outputDir);
});
}

// compile all resources with webpack and write them to the disk.
logger.log('Building storybook ...');
logger.info('Building storybook ...');
webpack(config).run((err, stats) => {
if (err || stats.hasErrors()) {
logger.error('Failed to build the storybook');
Expand Down
19 changes: 14 additions & 5 deletions app/angular/src/server/config.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
/* eslint-disable global-require, import/no-dynamic-require */
import fs from 'fs';
import path from 'path';
import { logger } from '@storybook/node-logger';
import loadBabelConfig from './babel_config';
import loadTsConfig from './ts_config';

// avoid ESLint errors
const logger = console;
import {
getAngularCliWebpackConfigOptions,
applyAngularCliWebpackConfig,
} from './angular-cli_config';

// `baseConfig` is a webpack configuration bundled with storybook.
// Storybook will look in the `configDir` directory
Expand Down Expand Up @@ -39,6 +41,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 +56,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
5 changes: 2 additions & 3 deletions app/angular/src/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ import path from 'path';
import fs from 'fs';
import chalk from 'chalk';
import shelljs from 'shelljs';
import { logger } from '@storybook/node-logger';
import storybook, { webpackValid } from './middleware';
import packageJson from '../../package.json';
import { parseList, getEnvConfig } from './utils';

process.env.NODE_ENV = process.env.NODE_ENV || 'development';

const logger = console;

program
.version(packageJson.version)
.option('-p, --port [number]', 'Port to run Storybook (Required)', parseInt)
Expand Down Expand Up @@ -105,7 +104,7 @@ if (program.staticDir) {
logger.error(`Error: no such directory to load static files: ${staticPath}`);
process.exit(-1);
}
logger.log(`=> Loading static files from: ${staticPath} .`);
logger.info(`=> Loading static files from: ${staticPath} .`);
app.use(express.static(staticPath, { index: false }));

const faviconPath = path.resolve(staticPath, 'favicon.ico');
Expand Down
4 changes: 1 addition & 3 deletions app/angular/src/server/ts_config.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import fs from 'fs';
import path from 'path';

// avoid ESLint errors
const logger = console;
import { logger } from '@storybook/node-logger';

function resolveTsConfig(tsConfigPath) {
if (!fs.existsSync(tsConfigPath)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Storyshots Component styles Component with styles 1`] = `
<storybook-dynamic-app-root
cfr={[Function CodegenComponentFactoryResolver]}
data={[Function Object]}
target={[Function ViewContainerRef_]}
>
<storybook-styled-component>
<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>


</storybook-styled-component>
</storybook-dynamic-app-root>
`;
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;
}