fix(storybook): linting react storybook files with eslint (#5146)

ISSUES CLOSED: #3867
This commit is contained in:
Juri Strumpflohner 2021-03-29 13:24:27 +02:00 committed by GitHub
parent ed94c4e1ec
commit ec2ebca40a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 172 additions and 1 deletions

View File

@ -27,6 +27,20 @@ describe('Storybook schematics', () => {
).toContain(`<title>Storybook</title>`);
}, 1000000);
it('should lint a React based storybook without errors', () => {
newProject();
const reactStorybookLib = uniq('test-ui-lib-react');
runCLI(`generate @nrwl/react:lib ${reactStorybookLib} --no-interactive`);
runCLI(
`generate @nrwl/react:storybook-configuration ${reactStorybookLib} --generateStories --no-interactive`
);
// build React lib
const output = runCLI(`run ${reactStorybookLib}:lint`);
expect(output).toContain('All files pass linting.');
}, 1000000);
it('should build a React based storybook that references another lib', () => {
const proj = newProject();

View File

@ -25,6 +25,11 @@
"version": "11.0.12",
"description": "Update storybook if installed and above 6",
"factory": "./src/migrations/update-11-0-12/update-storybook"
},
"update-11-5-3": {
"version": "11.5.3-beta.1",
"description": "Update react storybook lint config",
"factory": "./src/migrations/update-11-5-3/update-lint-ignores"
}
},
"packageJsonUpdates": {

View File

@ -4,5 +4,5 @@
"emitDecoratorMetadata": true
},
"exclude": ["../**/*.spec.ts" <% if(uiFramework === '@storybook/react') { %>, "../**/*.spec.js", "../**/*.spec.tsx", "../**/*.spec.jsx"<% } %>],
"include": ["../src/**/*"]
"include": ["../src/**/*", "*.js"]
}

View File

@ -0,0 +1,58 @@
import { Tree } from '@angular-devkit/schematics';
import { getFileContent } from '@nrwl/workspace/testing';
import { runMigration } from '../../utils/testing';
describe('Update 11-5-3', () => {
let tree: Tree;
beforeEach(async () => {
tree = Tree.empty();
// create workspace
tree.create(
'workspace.json',
JSON.stringify({
projects: {
['home-ui-react']: {
projectType: 'library',
root: 'libs/home/ui-react',
sourceRoot: 'libs/home/ui-react/src',
architect: {
storybook: {
builder: '@nrwl/storybook:storybook',
options: {
uiFramework: '@storybook/react',
port: 4400,
config: {
configFolder: 'libs/home/ui-react/.storybook',
},
},
},
},
},
},
})
);
tree.create(
'libs/home/ui-react/.storybook/tsconfig.json',
JSON.stringify({
extends: '../tsconfig.json',
include: ['../src/**/*'],
})
);
});
it(`should add storybook tsconfig to lint target and update tsconfigs in project for React project`, async () => {
tree = await runMigration('update-11-5-3', {}, tree);
expect(getFileContent(tree, 'libs/home/ui-react/.storybook/tsconfig.json'))
.toMatchInlineSnapshot(`
"{
\\"extends\\": \\"../tsconfig.json\\",
\\"include\\": [\\"../src/**/*\\", \\"./*.js\\"]
}
"
`);
});
});

View File

@ -0,0 +1,94 @@
import * as path from 'path';
import {
chain,
Tree,
SchematicContext,
Rule,
} from '@angular-devkit/schematics';
import {
formatFiles,
readJsonInTree,
updateWorkspaceInTree,
serializeJson,
} from '@nrwl/workspace';
import { isFramework, TsConfig } from '../../utils/utilities';
import { normalize } from '@angular-devkit/core';
interface ProjectDefinition {
root: string;
sourceRoot: string;
projectType: 'library' | 'application';
schematic?: Record<string, any>;
architect: Record<
string,
import('@angular-devkit/core').workspaces.TargetDefinition
>;
}
export default function (tree: Tree, context: SchematicContext) {
return chain([update, formatFiles()]);
}
function update(tree: Tree, context: SchematicContext): Rule {
return updateWorkspaceInTree((config, context, tree) => {
Object.entries<ProjectDefinition>(config.projects).forEach(
([projectName, projectConfig]) => {
updateTsconfigInclude(tree, context, { projectName, projectConfig });
}
);
return config;
});
}
function updateTsconfigInclude(
tree: Tree,
context: SchematicContext,
options: {
projectName: string;
projectConfig: ProjectDefinition;
}
) {
const architect = options.projectConfig.architect;
const paths = {
tsConfigStorybook: normalize(
path.join(options.projectConfig.root, '.storybook/tsconfig.json')
),
};
const hasStorybookConfig =
architect.storybook && tree.exists(paths.tsConfigStorybook);
if (!hasStorybookConfig) {
context.logger.info(
`${options.projectName}: no storybook configured. skipping migration...`
);
return;
}
const isReactProject = isFramework('react', {
uiFramework: architect.storybook.options?.uiFramework as Parameters<
typeof isFramework
>[1]['uiFramework'],
});
const tsConfig = {
storybook: readJsonInTree<TsConfig>(tree, paths.tsConfigStorybook),
};
if (isReactProject && Array.isArray(tsConfig.storybook.include)) {
tsConfig.storybook.include = uniqueArray([
...tsConfig.storybook.include,
'./*.js',
]);
tree.overwrite(paths.tsConfigStorybook, serializeJson(tsConfig.storybook));
}
}
function uniqueArray<T extends Array<any>>(value: T) {
return [...new Set(value)] as T;
}