Skip to content

Commit

Permalink
feat(react-native): upgrade @storybook/react-native to 6.5
Browse files Browse the repository at this point in the history
  • Loading branch information
xiongemi committed Apr 29, 2023
1 parent 85ddd8b commit 9a9d9a3
Show file tree
Hide file tree
Showing 19 changed files with 167 additions and 178 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"silent": {
"type": "boolean",
"description": "Silences output.",
"default": false
"default": false,
"x-deprecated": "No longer used. It will be silent as default."
}
},
"required": ["searchDir", "outputFile", "pattern"],
Expand Down
7 changes: 0 additions & 7 deletions packages/expo/src/utils/pod-install-task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,6 @@ export function podInstall(
}
logger.info(stdout);
if (stdout.includes('Pod installation complete')) {
// Remove build folder after pod install
if (buildFolder) {
buildFolder = join(iosDirectory, buildFolder);
if (existsSync(buildFolder)) {
rmdirSync(buildFolder, { recursive: true });
}
}
resolve();
} else {
reject(new Error(podInstallErrorMessage));
Expand Down
19 changes: 19 additions & 0 deletions packages/react-native/migrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@
"version": "16.0.0-beta.1",
"description": "Replace @nrwl/react-native with @nx/react-native",
"implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages"
},
"update-16-0-2-upgrade-storybook-6-5": {
"cli": "nx",
"version": "16.0.2-beta.0",
"description": "Upgrade @storybook/react-native to 6.5",
"implementation": "./src/migrations/update-16-0-2/upgrade-storybook-6-5"
}
},
"packageJsonUpdates": {
Expand Down Expand Up @@ -1291,6 +1297,19 @@
"alwaysAddToPackageJson": false
}
}
},
"16.0.2": {
"version": "16.0.2-beta.0",
"packages": {
"@react-native-async-storage/async-storage": {
"version": "1.18.1",
"alwaysAddToPackageJson": false
},
"react-native-safe-area-context": {
"version": "4.5.1",
"alwaysAddToPackageJson": false
}
}
}
}
}
4 changes: 3 additions & 1 deletion packages/react-native/src/executors/storybook/schema.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// options from https://github.com/elderfo/react-native-storybook-loader#options
export interface ReactNativeStorybookOptions {
searchDir: string[];
outputFile: string;
pattern: string;
/**
* @deprecated going to be removed in 17
*/
silent: boolean;
}
3 changes: 2 additions & 1 deletion packages/react-native/src/executors/storybook/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"silent": {
"type": "boolean",
"description": "Silences output.",
"default": false
"default": false,
"x-deprecated": "No longer used. It will be silent as default."
}
},
"required": ["searchDir", "outputFile", "pattern"]
Expand Down
77 changes: 21 additions & 56 deletions packages/react-native/src/executors/storybook/storybook.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@ import { join } from 'path';
import { ExecutorContext, logger } from '@nx/devkit';
import { fileExists } from '@nx/workspace/src/utilities/fileutils';
import * as chalk from 'chalk';
import { sync as globSync } from 'fast-glob';

import { ReactNativeStorybookOptions } from './schema';
import { ChildProcess, fork } from 'child_process';
import {
displayNewlyAddedDepsMessage,
syncDeps,
} from '../sync-deps/sync-deps.impl';
import { readFileSync, writeFileSync } from 'fs-extra';

let childProcess: ChildProcess;

export default async function* reactNatievStorybookExecutor(
export default async function* reactNativeStorybookExecutor(
options: ReactNativeStorybookOptions,
context: ExecutorContext
): AsyncGenerator<{ success: boolean }> {
Expand All @@ -35,6 +34,7 @@ export default async function* reactNatievStorybookExecutor(
context.root,
context.projectGraph,
[
`@storybook/react-native`,
'@storybook/addon-ondevice-actions',
'@storybook/addon-ondevice-backgrounds',
'@storybook/addon-ondevice-controls',
Expand All @@ -43,63 +43,28 @@ export default async function* reactNatievStorybookExecutor(
)
);

try {
await runCliStorybook(context.root, options);
yield { success: true };
} finally {
if (childProcess) {
childProcess.kill();
}
}
runCliStorybook(context.root, options);
yield { success: true };
}

function runCliStorybook(
export function runCliStorybook(
workspaceRoot: string,
options: ReactNativeStorybookOptions
) {
return new Promise((resolve, reject) => {
childProcess = fork(
join(
workspaceRoot,
'./node_modules/react-native-storybook-loader/out/rnstl-cli.js'
),
createStorybookOptions(options),
{
cwd: workspaceRoot,
}
);

// Ensure the child process is killed when the parent exits
process.on('exit', () => childProcess.kill());
process.on('SIGTERM', () => childProcess.kill());
const storiesFiles: string[] = options.searchDir.flatMap((dir) =>
globSync(join(workspaceRoot, dir, options.pattern))
);
if (storiesFiles.length === 0) {
logger.warn(`${chalk.bold.yellow('warn')} No stories found.`);
}

childProcess.on('error', (err) => {
reject(err);
});
childProcess.on('exit', (code) => {
if (code === 0) {
resolve(code);
} else {
reject(code);
}
});
});
}
const newContents = `// Auto-generated file created by nx
// DO NOT EDIT.
export function loadStories() {
return [
${storiesFiles.map((story) => `require('${story}')`).join(',\n')}
];
}`;

function createStorybookOptions(options) {
return Object.keys(options).reduce((acc, k) => {
const v = options[k];
if (typeof v === 'boolean') {
if (v === true) {
acc.push(`--${k}`);
}
} else if (Array.isArray(v)) {
v.forEach((value) => {
acc.push(`--${k}`, value);
});
} else {
acc.push(`--${k}`, v);
}
return acc;
}, []);
writeFileSync(join(workspaceRoot, options.outputFile), newContents);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,34 @@ import {
normalizePath,
Tree,
} from '@nx/devkit';
import * as ts from 'typescript';
import type * as ts from 'typescript';
import {
findExportDeclarationsForJsx,
getComponentNode,
getComponentPropsInterface,
} from '@nx/react/src/utils/ast-utils';
import { CreateComponentStoriesFileSchema } from './schema';

export function getArgsDefaultValue(property: ts.SyntaxKind): string {
const typeNameToDefault: Record<number, any> = {
[ts.SyntaxKind.StringKeyword]: "''",
[ts.SyntaxKind.NumberKeyword]: 0,
[ts.SyntaxKind.BooleanKeyword]: false,
};

const resolvedValue = typeNameToDefault[property];
if (typeof resolvedValue === undefined) {
return "''";
} else {
return resolvedValue;
}
import { getDefaultsForComponent } from '@nx/react/src/utils/component-props';
import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript';

let tsModule: typeof import('typescript');

export interface CreateComponentStoriesFileSchema {
project: string;
componentPath: string;
skipFormat?: boolean;
}

export function createComponentStoriesFile(
host: Tree,
{ project, componentPath }: CreateComponentStoriesFileSchema
) {
if (!tsModule) {
tsModule = ensureTypescript();
}
const proj = getProjects(host).get(project);
const sourceRoot = proj.sourceRoot;

const componentFilePath = joinPathFragments(sourceRoot, componentPath);

const componentDirectory = componentFilePath.replace(
componentFilePath.slice(componentFilePath.lastIndexOf('/')),
''
Expand Down Expand Up @@ -65,10 +62,10 @@ export function createComponentStoriesFile(
throw new Error(`Failed to read ${componentFilePath}`);
}

const sourceFile = ts.createSourceFile(
const sourceFile = tsModule.createSourceFile(
componentFilePath,
contents,
ts.ScriptTarget.Latest,
tsModule.ScriptTarget.Latest,
true
);

Expand Down Expand Up @@ -107,7 +104,7 @@ export function createComponentStoriesFile(
}
}

function findPropsAndGenerateFile(
export function findPropsAndGenerateFile(
host: Tree,
sourceFile: ts.SourceFile,
cmpDeclaration: ts.Node,
Expand All @@ -117,37 +114,10 @@ function findPropsAndGenerateFile(
fileExt: string,
fromNodeArray?: boolean
) {
const propsInterface = getComponentPropsInterface(sourceFile, cmpDeclaration);

let propsTypeName: string = null;
let props: {
name: string;
defaultValue: any;
}[] = [];
let argTypes: {
name: string;
type: string;
actionText: string;
}[] = [];

if (propsInterface) {
propsTypeName = propsInterface.name.text;
props = propsInterface.members.map((member: ts.PropertySignature) => {
if (member.type.kind === ts.SyntaxKind.FunctionType) {
argTypes.push({
name: (member.name as ts.Identifier).text,
type: 'action',
actionText: `${(member.name as ts.Identifier).text} executed!`,
});
} else {
return {
name: (member.name as ts.Identifier).text,
defaultValue: getArgsDefaultValue(member.type.kind),
};
}
});
props = props.filter((p) => p && p.defaultValue !== undefined);
}
const { propsTypeName, props, argTypes } = getDefaultsForComponent(
sourceFile,
cmpDeclaration
);

generateFiles(
host,
Expand All @@ -164,7 +134,6 @@ function findPropsAndGenerateFile(
componentName: (cmpDeclaration as any).name.text,
isPlainJs,
fileExt,
hasActions: argTypes && argTypes.length,
}
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
<% if (hasActions) { %>
import { action } from '@storybook/addon-actions';
<% } %>
import { storiesOf } from '@storybook/react-native';
import React from 'react';
import {
<%= componentName %>
<% if ( propsTypeName ) { %>, <%= propsTypeName %> <% } %>
} from './<%= componentImportFileName %>';
<% if ( !isPlainJs ) { %>import type { Meta } from '@storybook/react-native';<% } %>
import<% if ( !isPlainJs ) { %> { <% } %> <%= componentName %> <% if ( !isPlainJs ) { %> } <% } %> from './<%= componentImportFileName %>';

<% if (hasActions) { %>
const actions = {<% for (let argType of argTypes) { %>
<%= argType.name %>: action('<%- argType.actionText %>'),
<% } %>};
<% if ( isPlainJs ) { %>
export default {
component: <%= componentName %>,
title: '<%= componentName %>',<% if ( argTypes && argTypes.length > 0 ) { %>
argTypes: {<% for (let argType of argTypes) { %>
<%= argType.name %>: { <%- argType.type %> : "<%- argType.actionText %>" },<% } %>
}
<% } %>
};
<% } %>

const props <% if ( propsTypeName ) { %>:<%= propsTypeName %><% } %> = {<% for (let prop of props) { %>
<%= prop.name %>: <%- prop.defaultValue %>,
<% } %>};

storiesOf('<%= componentName %>', module)
.add('Primary', () => (
<<%= componentName %> {...props} <% if (hasActions) { %> {...actions} <% } %>/>
));
<% if ( !isPlainJs ) { %>
const Story: Meta<typeof <%= componentName %>> = {
component: <%= componentName %>,
title: '<%= componentName %>',<% if ( argTypes && argTypes.length > 0 ) { %>
argTypes: {<% for (let argType of argTypes) { %>
<%= argType.name %>: { <%- argType.type %> : "<%- argType.actionText %>" },<% } %>
}
<% } %>
};
export default Story;
<% } %>

export const Primary = {
args: {<% for (let prop of props) { %>
<%= prop.name %>: <%- prop.defaultValue %>,<% } %>
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,6 @@ describe('react-native:storybook-configuration', () => {
let appTree;

beforeEach(async () => {
// jest.spyOn(fileUtils, 'readPackageJson').mockReturnValue({
// devDependencies: {
// '@storybook/addon-essentials': '*',
// '@storybook/react-native': '*',
// '@storybook/addon-ondevice-actions': '*',
// '@storybook/addon-ondevice-knobs': '*',
// },
// });

jest.spyOn(logger, 'warn').mockImplementation(() => {});
jest.spyOn(logger, 'debug').mockImplementation(() => {});
});
Expand Down
Loading

0 comments on commit 9a9d9a3

Please sign in to comment.