Skip to content

Commit

Permalink
feat(storybook): choose to generate ts config (#10572)
Browse files Browse the repository at this point in the history
ISSUES CLOSED: #10394
  • Loading branch information
mandarini committed Jun 3, 2022
1 parent d53000a commit 1de896f
Show file tree
Hide file tree
Showing 17 changed files with 209 additions and 12 deletions.
7 changes: 6 additions & 1 deletion docs/generated/packages/react-native.json
Expand Up @@ -355,7 +355,12 @@
},
"js": {
"type": "boolean",
"description": "Generate JavaScript files rather than TypeScript files.",
"description": "Generate JavaScript story files rather than TypeScript story files.",
"default": false
},
"tsConfiguration": {
"type": "boolean",
"description": "Configure your project with TypeScript. Generate main.ts and preview.ts files, instead of main.js and preview.js.",
"default": false
},
"linter": {
Expand Down
7 changes: 6 additions & 1 deletion docs/generated/packages/react.json
Expand Up @@ -625,7 +625,12 @@
},
"js": {
"type": "boolean",
"description": "Generate JavaScript files rather than TypeScript files.",
"description": "Generate JavaScript story files rather than TypeScript story files.",
"default": false
},
"tsConfiguration": {
"type": "boolean",
"description": "Configure your project with TypeScript. Generate main.ts and preview.ts files, instead of main.js and preview.js.",
"default": false
},
"linter": {
Expand Down
7 changes: 6 additions & 1 deletion docs/generated/packages/storybook.json
Expand Up @@ -139,7 +139,12 @@
},
"js": {
"type": "boolean",
"description": "Generate JavaScript files rather than TypeScript files.",
"description": "Generate JavaScript story files rather than TypeScript story files.",
"default": false
},
"tsConfiguration": {
"type": "boolean",
"description": "Configure your project with TypeScript. Generate main.ts and preview.ts files, instead of main.js and preview.js.",
"default": false
},
"standaloneConfig": {
Expand Down
Expand Up @@ -4,6 +4,7 @@ export interface StorybookConfigureSchema {
name: string;
generateStories?: boolean;
js?: boolean;
tsConfiguration?: boolean;
linter?: Linter;
standaloneConfig?: boolean;
}
Expand Up @@ -22,7 +22,12 @@
},
"js": {
"type": "boolean",
"description": "Generate JavaScript files rather than TypeScript files.",
"description": "Generate JavaScript story files rather than TypeScript story files.",
"default": false
},
"tsConfiguration": {
"type": "boolean",
"description": "Configure your project with TypeScript. Generate main.ts and preview.ts files, instead of main.js and preview.js.",
"default": false
},
"linter": {
Expand Down
Expand Up @@ -6,6 +6,7 @@ export interface StorybookConfigureSchema {
generateStories?: boolean;
generateCypressSpecs?: boolean;
js?: boolean;
tsConfiguration?: boolean;
linter?: Linter;
cypressDirectory?: string;
standaloneConfig?: boolean;
Expand Down
Expand Up @@ -38,7 +38,12 @@
},
"js": {
"type": "boolean",
"description": "Generate JavaScript files rather than TypeScript files.",
"description": "Generate JavaScript story files rather than TypeScript story files.",
"default": false
},
"tsConfiguration": {
"type": "boolean",
"description": "Configure your project with TypeScript. Generate main.ts and preview.ts files, instead of main.js and preview.js.",
"default": false
},
"linter": {
Expand Down
Expand Up @@ -81,6 +81,31 @@ describe('@nrwl/storybook:configuration', () => {
).toBeFalsy();
});

it('should generate TypeScript Configuration files', async () => {
await configurationGenerator(tree, {
name: 'test-ui-lib',
uiFramework: '@storybook/angular',
standaloneConfig: false,
tsConfiguration: true,
});

// Root
expect(tree.exists('.storybook/tsconfig.json')).toBeTruthy();
expect(tree.exists('.storybook/main.ts')).toBeTruthy();
const rootStorybookTsconfigJson = readJson<TsConfig>(
tree,
'.storybook/tsconfig.json'
);
expect(rootStorybookTsconfigJson.extends).toBe('../tsconfig.base.json');

// Local
expect(
tree.exists('libs/test-ui-lib/.storybook/tsconfig.json')
).toBeTruthy();
expect(tree.exists('libs/test-ui-lib/.storybook/main.ts')).toBeTruthy();
expect(tree.exists('libs/test-ui-lib/.storybook/preview.ts')).toBeTruthy();
});

it('should extend from root tsconfig.json when no tsconfig.base.json', async () => {
tree.rename('tsconfig.base.json', 'tsconfig.json');

Expand Down Expand Up @@ -350,4 +375,39 @@ describe('@nrwl/storybook:configuration', () => {
readJson(tree, 'libs/test-ui-lib2/.storybook/tsconfig.json').files
).toMatchSnapshot();
});

it('should generate TS config for project if root config is TS', async () => {
await configurationGenerator(tree, {
name: 'test-ui-lib',
uiFramework: '@storybook/angular',
standaloneConfig: false,
tsConfiguration: true,
});

const newContents = `module.exports = {
stories: [],
addons: ['@storybook/addon-essentials', 'new-addon'],
};
`;
// Setup a new lib
await libraryGenerator(tree, {
name: 'test-ui-lib-2',
standaloneConfig: false,
});

tree.write('.storybook/main.ts', newContents);
await configurationGenerator(tree, {
name: 'test-ui-lib-2',
uiFramework: '@storybook/angular',
standaloneConfig: false,
});

expect(tree.read('.storybook/main.ts', 'utf-8')).toEqual(newContents);
expect(tree.exists('libs/test-ui-lib-2/.storybook/main.ts')).toBeTruthy();
expect(
tree.exists('libs/test-ui-lib-2/.storybook/preview.ts')
).toBeTruthy();
expect(tree.exists('libs/test-ui-lib-2/.storybook/main.js')).toBeFalsy();
expect(tree.exists('libs/test-ui-lib-2/.storybook/preview.js')).toBeFalsy();
});
});
10 changes: 8 additions & 2 deletions packages/storybook/src/generators/configuration/configuration.ts
Expand Up @@ -39,8 +39,14 @@ export async function configurationGenerator(
});
tasks.push(initTask);

createRootStorybookDir(tree, schema.js);
createProjectStorybookDir(tree, schema.name, schema.uiFramework, schema.js);
createRootStorybookDir(tree, schema.js, schema.tsConfiguration);
createProjectStorybookDir(
tree,
schema.name,
schema.uiFramework,
schema.js,
schema.tsConfiguration
);
configureTsProjectConfig(tree, schema);
configureTsSolutionConfig(tree, schema);
updateLintConfig(tree, schema);
Expand Down
@@ -0,0 +1,28 @@
import { rootMain } from '<%= offsetFromRoot %>../.storybook/main';
import type { StorybookConfig, Options } from '@storybook/core-common';

const config: StorybookConfig = {
...rootMain,
<% if (useWebpack5) { %>
core: { ...rootMain.core, builder: 'webpack5' },
<% } %>

stories: [
...rootMain.stories,
'../src/app/**/*.stories.mdx',
'../src/app/**/*.stories.@(js|jsx|ts|tsx)',
],
addons: [...(rootMain.addons || []) <% if(uiFramework === '@storybook/react') { %>, '@nrwl/react/plugins/storybook' <% } %><% if(uiFramework === '@storybook/react-native') { %>, '@storybook/addon-ondevice-actions', '@storybook/addon-ondevice-backgrounds', '@storybook/addon-ondevice-controls', '@storybook/addon-ondevice-notes' <% } %>],
webpackFinal: async (config, { configType }: Options) => {
// apply any global webpack configs that might have been specified in .storybook/main.ts
if (rootMain.webpackFinal) {
config = await rootMain.webpackFinal(config, { configType } as Options);
}

// add your own webpack tweaks if needed

return config;
},
};

module.exports = config;
@@ -0,0 +1,14 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"emitDecoratorMetadata": true
<% if(uiFramework === '@storybook/react') { %>, "outDir": "" <% } %>
},
<% if(uiFramework === '@storybook/react') { %>"files": [
"<%= offsetFromRoot %>../node_modules/@nrwl/react/typings/styled-jsx.d.ts",
"<%= offsetFromRoot %>../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"<%= offsetFromRoot %>../node_modules/@nrwl/react/typings/image.d.ts"
],<% } %>
"exclude": ["../**/*.spec.ts" <% if(uiFramework === '@storybook/react') { %>, "../**/*.spec.js", "../**/*.spec.tsx", "../**/*.spec.jsx"<% } %>],
"include": ["../src/**/*", "*.js", "*.ts" <% if(uiFramework === '@storybook/react-native') { %>, "*.tsx"<% } %>]
}
@@ -0,0 +1,12 @@
import type { StorybookConfig } from '@storybook/core-common';

export const rootMain: StorybookConfig = {
stories: [],
addons: ['@storybook/addon-essentials'],
// webpackFinal: async (config, { configType }) => {
// // Make whatever fine-grained changes you need that should apply to all storybook configs

// // Return the altered config
// return config;
// },
};
@@ -0,0 +1,14 @@
{
"extends": "../<%= rootTsConfigPath %>",
"exclude": [
"../**/*.spec.js",
"../**/*.test.js",
"../**/*.spec.ts",
"../**/*.test.ts",
"../**/*.spec.tsx",
"../**/*.test.tsx",
"../**/*.spec.jsx",
"../**/*.test.jsx"
],
"include": ["../**/*"]
}
Expand Up @@ -9,6 +9,7 @@ export interface StorybookConfigureSchema {
configureCypress?: boolean;
linter?: Linter;
js?: boolean;
tsConfiguration?: boolean;
cypressDirectory?: string;
standaloneConfig?: boolean;
projectBuildConfig?: string;
Expand Down
7 changes: 6 additions & 1 deletion packages/storybook/src/generators/configuration/schema.json
Expand Up @@ -41,7 +41,12 @@
},
"js": {
"type": "boolean",
"description": "Generate JavaScript files rather than TypeScript files.",
"description": "Generate JavaScript story files rather than TypeScript story files.",
"default": false
},
"tsConfiguration": {
"type": "boolean",
"description": "Configure your project with TypeScript. Generate main.ts and preview.ts files, instead of main.js and preview.js.",
"default": false
},
"standaloneConfig": {
Expand Down
38 changes: 34 additions & 4 deletions packages/storybook/src/generators/configuration/util-functions.ts
Expand Up @@ -238,7 +238,11 @@ export function normalizeSchema(
};
}

export function createRootStorybookDir(tree: Tree, js: boolean) {
export function createRootStorybookDir(
tree: Tree,
js: boolean,
tsConfiguration: boolean
) {
if (tree.exists('.storybook')) {
logger.warn(
`.storybook folder already exists at root! Skipping generating files in it.`
Expand All @@ -247,7 +251,10 @@ export function createRootStorybookDir(tree: Tree, js: boolean) {
}
logger.debug(`adding .storybook folder to the root directory`);

const templatePath = join(__dirname, './root-files');
const templatePath = join(
__dirname,
tsConfiguration ? './root-files-ts' : './root-files'
);
generateFiles(tree, templatePath, '', {
rootTsConfigPath: getRootTsConfigPathInTree(tree),
});
Expand All @@ -261,8 +268,28 @@ export function createProjectStorybookDir(
tree: Tree,
projectName: string,
uiFramework: StorybookConfigureSchema['uiFramework'],
js: boolean
js: boolean,
tsConfiguration: boolean
) {
// Check if root main file is .ts or .js
if (tree.exists('.storybook/main.ts')) {
logger.info(
`The root Storybook configuration is in TypeScript,
so Nx will generate TypeScript Storybook configuration files
in this project's .storybook folder as well.`
);
tsConfiguration = true;
} else {
if (tree.exists('.storybook/main.js')) {
logger.info(
`The root Storybook configuration is in JavaScript,
so Nx will generate JavaScript Storybook configuration files
in this project's .storybook folder as well.`
);
tsConfiguration = false;
}
}

const { root, projectType } = readProjectConfiguration(tree, projectName);
const projectDirectory = projectType === 'application' ? 'app' : 'lib';

Expand All @@ -276,7 +303,10 @@ export function createProjectStorybookDir(
}

logger.debug(`adding .storybook folder to ${projectDirectory}`);
const templatePath = join(__dirname, './project-files');
const templatePath = join(
__dirname,
tsConfiguration ? './project-files-ts' : './project-files'
);

generateFiles(tree, templatePath, root, {
tmpl: '',
Expand Down

1 comment on commit 1de896f

@vercel
Copy link

@vercel vercel bot commented on 1de896f Jun 3, 2022

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

nx-dev – ./

nx-dev-git-master-nrwl.vercel.app
nx-dev-nrwl.vercel.app
nx-five.vercel.app
nx.dev

Please sign in to comment.