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

feat: support storybook 6.2.x-beta #165

Merged
merged 11 commits into from
Feb 23, 2021
Merged
Show file tree
Hide file tree
Changes from 7 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
7 changes: 7 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ jobs:
- run:
name: integration tests
command: yarn integration
testNext:
executor: base
steps:
- init
- run:
name: integration test for next releases
command: yarn integrationNext
deploy:
executor: base
steps:
Expand Down
19 changes: 16 additions & 3 deletions bin/integration.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,32 @@ function preparePackage {

function preparePackages {
preparePackage integration-tests
preparePackage integration-tests-storybook
# installStorybook $1
preparePackage integration-tests-storybook-6_1_x
preparePackage integration-tests-storybook-6_2_x
}

function integration {
preparePackages
cd $PROJECT_ROOT && yarn jest --runInBand --config integration.jest.config.js --forceExit ${@}
preparePackages $1
runIntegration ${@:2}
}

function runIntegration {
cd $PROJECT_ROOT && yarn jest --runInBand --config integration.jest.config.js --forceExit ${@}
}

function integrationLocal {
preparePackages
cd $PROJECT_ROOT && yarn jest --runInBand --config integration.local.jest.config.js --forceExit ${@}
}

function installStorybook {
lerna add -D --scope @bojagi/integration-tests-storybook @storybook/addon-actions@$1
lerna add -D --scope @bojagi/integration-tests-storybook @storybook/addon-essentials@$1
lerna add -D --scope @bojagi/integration-tests-storybook @storybook/addon-links@$1
lerna add -D --scope @bojagi/integration-tests-storybook @storybook/react@$1
}

case $1 in
integration )
integration ${@:2}
Expand Down
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@
"private": true,
"scripts": {
"test": "jest",
"integration": "./bin/integration.sh integration",
"integration": "./bin/integration.sh integration latest",
"integrationLocal": "./bin/integration.sh integrationLocal",
"integrationNext": "./bin/integration.sh integration next",
"build": "tsc -b",
"deploy": "lerna publish from-git -y",
"publish": "./bin/publish.sh",
"lint": "eslint packages/*/src/**/*.ts",
"updateReadmes": "lerna exec \"cp \\$LERNA_ROOT_PATH/README.md \\$PWD/README.md\""
},
"license" : "BSD-3-Clause",
"license": "BSD-3-Clause",
"devDependencies": {
"@types/jest": "24.0.15",
"@types/node": "14.14.31",
"@types/react": "16.9.41",
"@types/node": "12.12.6",
"@types/request-promise": "4.1.45",
"@typescript-eslint/eslint-plugin": "2.21.0",
"@typescript-eslint/eslint-plugin-tslint": "2.21.0",
Expand Down Expand Up @@ -44,5 +45,6 @@
},
"workspaces": [
"packages/*"
]
],
"nohoist": ["**/@storybook/**"]
}
1 change: 1 addition & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"node-emoji": "^1.10.0",
"open": "^7.3.0",
"rimraf": "^2.6.3",
"semver": "^7.3.4",
"simple-git": "^2.4.0",
"webpack-dev-server": "^3.11.2",
"webpack-merge": "^5.1.4"
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/config/getProjectWebpackConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as pathUtils from 'path';
import { CiSettings } from './getCiSettings';
import { BaseConfig } from './types';
import { NonVerboseError } from '../errors';
import { softRequireResolve } from '../utils/softRequireResolve';
import { softRequireResolve } from '../utils/requireUtils';
import { getStorybookProjectWebpackConfig } from '../storybook';

import webpack = require('webpack');
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export enum DebugNamespaces {
UPLOAD = 'upload',
VALIDATE = 'validate',
WEBPACK_CONFIG = 'webpackConfig',
REQUIRE = 'require',
}

const debuggers = Object.values(DebugNamespaces).reduce(
Expand Down
25 changes: 25 additions & 0 deletions packages/cli/src/storybook/getProjectWebpackConfig/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* eslint-disable camelcase */
import webpack from 'webpack';
import * as semver from 'semver';
import { getStorybookFramework, getStorybookVersion } from '../storybookUtils';
import { getV_6_1_X_StorybookProjectWebpackConfig } from './v6.1.x';
import { getV_6_2_X_StorybookProjectWebpackConfig } from './v6.2.x';
import debuggers, { DebugNamespaces } from '../../debug';

const debug = debuggers[DebugNamespaces.STORYBOOK];

export async function getStorybookProjectWebpackConfig(): Promise<webpack.Configuration | void> {
const framework = getStorybookFramework();
if (framework) {
debug(`storybook framework detected: ${framework}`);
const version = getStorybookVersion(framework);
debug(`storybook version: ${version}`);
if (semver.gte(semver.coerce(version), '6.2.0')) {
debug('version greater than 6.2.0, using V_6_2_X configuration');
return getV_6_2_X_StorybookProjectWebpackConfig(framework);
}
debug('version below 6.2.0, using V_6_1_X configuration');
return getV_6_1_X_StorybookProjectWebpackConfig(framework);
}
return undefined;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import webpack from 'webpack';

export function replaceDefaultMediaLoader(rules: webpack.RuleSetRule[]): webpack.RuleSetRule[] {
const defaultAssertLoaderIndex = rules.findIndex(
rule =>
rule.loader?.toString().includes('file-loader') &&
rule.test?.toString().includes('svg|ico|jpg|jpeg|png|apng|gif')
);

// if we find storybooks default asset loader we make sure to use the url loader instead
if (defaultAssertLoaderIndex >= 0) {
const ruleCopy = { ...rules[defaultAssertLoaderIndex] };

const newRules = [...rules];
newRules.splice(defaultAssertLoaderIndex, 1, {
...ruleCopy,
loader: 'url-loader', // we bundle all small assets into the js
options: { limit: 10000, name: 'static/media/[name].[hash:8].[ext]', esModule: false },
});
return newRules;
}
return rules;
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { getStorybookLoadOptions } from './storybookUtils';
import { replaceWebpackRules } from '../utils/replaceWebpackRules';
import { getSbOption, getSbCliOptions } from './getSbOption';
import { getPackageFolder } from '../utils/getPackageFolder';
import * as path from 'path';
import { getStorybookFrameworkLoadOptions } from '../storybookUtils';
import { replaceWebpackRules } from '../../utils/replaceWebpackRules';
import { getSbOption, getSbCliOptions } from '../getSbOption';
import { getPackageFolder } from '../../utils/getPackageFolder';
import { replaceDefaultMediaLoader } from './replaceLoaders';
import { StorybookFramework } from '../types';

import webpack = require('webpack');

const path = require('path');

async function getWebpackConfig(loadOptions) {
// we have to load all those libs dynamically as they are all optional
// eslint-disable-next-line import/no-extraneous-dependencies
Expand Down Expand Up @@ -36,39 +37,17 @@ async function getWebpackConfig(loadOptions) {
return replaceWebpackRules(webpackConfig, replaceDefaultMediaLoader);
}

export async function getStorybookProjectWebpackConfig(): Promise<webpack.Configuration | void> {
const loadConfig = getStorybookLoadOptions();
if (loadConfig) {
return getWebpackConfig(loadConfig);
}
// eslint-disable-next-line camelcase
export async function getV_6_1_X_StorybookProjectWebpackConfig(
framework: StorybookFramework
): Promise<webpack.Configuration | void> {
const loadConfig = getStorybookFrameworkLoadOptions(framework);

return undefined;
return getWebpackConfig(loadConfig);
}

function getOutputDir(givenOutputDir) {
return path.isAbsolute(givenOutputDir)
? givenOutputDir
: path.join(process.cwd(), givenOutputDir);
}

function replaceDefaultMediaLoader(rules: webpack.RuleSetRule[]): webpack.RuleSetRule[] {
const defaultAssertLoaderIndex = rules.findIndex(
rule =>
rule.loader?.toString().includes('file-loader') &&
rule.test?.toString().includes('svg|ico|jpg|jpeg|png|apng|gif')
);

// if we find storybooks default asset loader we make sure to use the url loader instead
if (defaultAssertLoaderIndex >= 0) {
const ruleCopy = { ...rules[defaultAssertLoaderIndex] };

const newRules = [...rules];
newRules.splice(defaultAssertLoaderIndex, 1, {
...ruleCopy,
loader: 'url-loader', // we bundle all small assets into the js
options: { limit: 10000, name: 'static/media/[name].[hash:8].[ext]', esModule: false },
});
return newRules;
}
return rules;
}
74 changes: 74 additions & 0 deletions packages/cli/src/storybook/getProjectWebpackConfig/v6.2.x.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import * as path from 'path';
import { getStorybookFrameworkLoadOptions } from '../storybookUtils';
import { replaceWebpackRules } from '../../utils/replaceWebpackRules';
import { getSbOption, getSbCliOptions } from '../getSbOption';
import { getPackageFolder } from '../../utils/getPackageFolder';
import { replaceDefaultMediaLoader } from './replaceLoaders';
import { StorybookFramework } from '../types';

import webpack = require('webpack');

async function getWebpackConfig(loadOptions) {
// we have to load all those libs dynamically as they are all optional
const {
getPreviewBuilder,
// eslint-disable-next-line import/no-extraneous-dependencies
} = require('@storybook/core-server/dist/cjs/utils/get-preview-builder');
// eslint-disable-next-line import/no-extraneous-dependencies
const { loadAllPresets } = require('@storybook/core-common');
const cliOptions = getSbCliOptions();
const configDir = getSbOption('configDir', './.storybook');

const previewBuilder = await getPreviewBuilder(configDir);

const coreOptions = {
...loadOptions,
...cliOptions,
configType: 'PRODUCTION',
outputDir: getOutputDir(getSbOption('outputDir', './storybook-static')),
configDir,
ignorePreview: !!cliOptions.previewUrl,
docsMode: !!cliOptions.docs,
};

const presets = await loadAllPresets({
...coreOptions,
corePresets: [
require.resolve('@storybook/core-server/dist/cjs/presets/common-preset'),
require.resolve('@storybook/core-server/dist/cjs/presets/manager-preset'),
...previewBuilder.corePresets,
require.resolve('@storybook/core-server/dist/cjs/presets/babel-cache-preset'),
],
overridePresets: previewBuilder.overridePresets,
});

const fullOptions = {
...coreOptions,
presets,
};

const webpackConfig = await previewBuilder.getConfig(fullOptions);

// Add node_modules of SB core in case resolvers are placed there by npm/yarn
webpackConfig.resolveLoader = webpackConfig.resolveLoader || {};
const corePackagePath = getPackageFolder('@storybook/core-server');
webpackConfig.resolveLoader.modules = webpackConfig.resolveLoader?.modules || ['node_modules'];
webpackConfig.resolveLoader.modules.push(path.join(corePackagePath, 'node_modules'));

return replaceWebpackRules(webpackConfig, replaceDefaultMediaLoader);
}

// eslint-disable-next-line camelcase
export async function getV_6_2_X_StorybookProjectWebpackConfig(
framework: StorybookFramework
): Promise<webpack.Configuration | void> {
const loadConfig = getStorybookFrameworkLoadOptions(framework);

return getWebpackConfig(loadConfig);
}

function getOutputDir(givenOutputDir) {
return path.isAbsolute(givenOutputDir)
? givenOutputDir
: path.join(process.cwd(), givenOutputDir);
}
21 changes: 17 additions & 4 deletions packages/cli/src/storybook/storybookUtils.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { softRequireResolve } from '../utils/softRequireResolve';
import { requireFromPaths, softRequireResolve } from '../utils/requireUtils';
import { StorybookFramework } from './types';

const frameworkToPackageMap = {
[StorybookFramework.REACT]: '@storybook/react',
};

const frameworkToLoadOptionsMap = {
[StorybookFramework.REACT]: '@storybook/react/dist/server/options',
[StorybookFramework.REACT]: [
'@storybook/react/dist/server/options',
'@storybook/react/dist/cjs/server/options',
],
};

export function getStorybookLoadOptions() {
Expand All @@ -22,11 +25,21 @@ export function getStorybookLoadOptions() {
}

export function getStorybookFrameworkLoadOptions(framework: StorybookFramework) {
return require(frameworkToLoadOptionsMap[framework]).default;
const possibleImports = frameworkToLoadOptionsMap[framework];

return requireFromPaths<any>(possibleImports).default;
}

export function getStorybookFramework(): StorybookFramework | undefined {
const sbFrameworkKey = Object.keys(StorybookFramework).find(storybookFrameworkIsInstalled);
if (sbFrameworkKey) {
return StorybookFramework[sbFrameworkKey];
}
return undefined;
}

export function storybookIsInstalled(): boolean {
return !!Object.keys(StorybookFramework).find(storybookFrameworkIsInstalled);
return !!getStorybookFramework();
}

export function storybookFrameworkIsInstalled(framework: StorybookFramework): boolean {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as path from 'path';
import { softRequireResolve } from './softRequireResolve';
import { softRequireResolve } from './requireUtils';

test('return the path if file exists', () => {
expect(softRequireResolve('./softRequireResolve')).toEqual(
Expand Down
33 changes: 33 additions & 0 deletions packages/cli/src/utils/requireUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import debuggers, { DebugNamespaces } from '../debug';

const debug = debuggers[DebugNamespaces.REQUIRE];

export function softRequireResolve(pathName) {
try {
return require.resolve(pathName);
} catch {
return undefined;
}
}

export function requireFromPaths<T>(paths: Array<string>): T {
for (let i = 0; i < paths.length; i += 1) {
try {
return require(paths[i]);
} catch (e) {
debug(`not found under ${paths[i]}`);
}
}
throw new Error(`module not found - ${paths.join(',')}`);
}

export function resolveFromPaths(paths: Array<string>): string {
for (let i = 0; i < paths.length; i += 1) {
try {
return require.resolve(paths[i]);
} catch (e) {
debug(`not found under ${paths[i]}`);
}
}
throw new Error(`module not found - ${paths.join(',')}`);
}
7 changes: 0 additions & 7 deletions packages/cli/src/utils/softRequireResolve.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/integration-test-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function writeTestResult(name, content) {

export function getTestResult(name) {
return JSON.parse(
fs.readFileSync(path.resolve(TEST_RESULT_FOLDER, `${name}.json`), { encoding: 'uft8' })
fs.readFileSync(path.resolve(TEST_RESULT_FOLDER, `${name}.json`), { encoding: 'utf8' })
);
}

Expand Down
Loading