fix(react-native): fix react native storybook for lib (#29210)
<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior <!-- This is the behavior we have today --> ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes https://github.com/nrwl/nx/issues/28802
This commit is contained in:
parent
c2eae0e297
commit
b4aadccac8
@ -93,7 +93,7 @@
|
||||
"type": "string",
|
||||
"enum": ["vite", "webpack"],
|
||||
"x-prompt": "Which bundler do you want to use to build the application?",
|
||||
"default": "webpack",
|
||||
"default": "vite",
|
||||
"x-priority": "important"
|
||||
}
|
||||
},
|
||||
|
||||
@ -73,6 +73,7 @@
|
||||
}
|
||||
},
|
||||
"required": ["project"],
|
||||
"examplesFile": "This generator will set up Storybook for your **React Native** project.\n\n```bash\nnx g @nx/react-native:storybook-configuration project-name\n```\n\nWhen running this generator, you will be prompted to provide the following:\n\n- The `name` of the project you want to generate the configuration for.\n- Whether you want to set up [Storybook interaction tests](https://storybook.js.org/docs/react/writing-tests/interaction-testing) (`interactionTests`). If you choose `yes`, a `play` function will be added to your stories, and all the necessary dependencies will be installed. Also, a `test-storybook` target will be generated in your project's `project.json`, with a command to invoke the [Storybook `test-runner`](https://storybook.js.org/docs/react/writing-tests/test-runner). You can read more about this in the [Nx Storybook interaction tests documentation page](/recipes/storybook/storybook-interaction-tests#setup-storybook-interaction-tests)..\n- Whether you want to `generateStories` for the components in your project. If you choose `yes`, a `.stories.ts` file will be generated next to each of your components in your project.\n\nYou must provide a `name` for the generator to work.\n\nBy default, this generator will also set up [Storybook interaction tests](https://storybook.js.org/docs/react/writing-tests/interaction-testing). If you don't want to set up Storybook interaction tests, you can pass the `--interactionTests=false` option, but it's not recommended.\n\nThere are a number of other options available. Let's take a look at some examples.\n\n## Examples\n\n### Generate Storybook configuration\n\n```bash\nnx g @nx/react-native:storybook-configuration ui\n```\n\nThis will generate Storybook configuration for the `ui` project using TypeScript for the Storybook configuration files (the files inside the `.storybook` directory, eg. `.storybook/main.ts`).\n\n### Ignore certain paths when generating stories\n\n```bash\nnx g @nx/react-native:storybook-configuration ui --generateStories=true --ignorePaths=libs/ui/src/not-stories/**,**/**/src/**/*.other.*,apps/my-app/**/*.something.ts\n```\n\nThis will generate a Storybook configuration for the `ui` project and generate stories for all components in the `libs/ui/src/lib` directory, except for the ones in the `libs/ui/src/not-stories` directory, and the ones in the `apps/my-app` directory that end with `.something.ts`, and also for components that their file name is of the pattern `*.other.*`.\n\nThis is useful if you have a project that contains components that are not meant to be used in isolation, but rather as part of a larger component.\n\nBy default, Nx will ignore the following paths:\n\n```text\n*.stories.ts, *.stories.tsx, *.stories.js, *.stories.jsx, *.stories.mdx\n```\n\nbut you can change this behaviour easily, as explained above.\n\n### Generate stories using JavaScript instead of TypeScript\n\n```bash\nnx g @nx/react-native:storybook-configuration ui --generateStories=true --js=true\n```\n\nThis will generate stories for all the components in the `ui` project using JavaScript instead of TypeScript. So, you will have `.stories.js` files next to your components.\n\n### Generate Storybook configuration using JavaScript\n\n```bash\nnx g @nx/react-native:storybook-configuration ui --tsConfiguration=false\n```\n\nBy default, our generator generates TypeScript Storybook configuration files. You can choose to use JavaScript for the Storybook configuration files of your project (the files inside the `.storybook` directory, eg. `.storybook/main.js`).\n",
|
||||
"presets": []
|
||||
},
|
||||
"description": "Set up Storybook for a React Native application or library.",
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
"type": "string",
|
||||
"enum": ["vite", "webpack"],
|
||||
"x-prompt": "Which bundler do you want to use to build the application?",
|
||||
"default": "webpack",
|
||||
"default": "vite",
|
||||
"x-priority": "important"
|
||||
}
|
||||
},
|
||||
|
||||
@ -9,21 +9,42 @@ import {
|
||||
fileExists,
|
||||
checkFilesExist,
|
||||
runE2ETests,
|
||||
updateFile,
|
||||
} from 'e2e/utils';
|
||||
|
||||
describe('@nx/react-native', () => {
|
||||
let proj: string;
|
||||
let appName: string;
|
||||
let libName: string;
|
||||
let componentName: string;
|
||||
|
||||
beforeAll(() => {
|
||||
newProject();
|
||||
proj = newProject();
|
||||
appName = uniq('app');
|
||||
runCLI(
|
||||
`generate @nx/react-native:app ${appName} --install=false --no-interactive --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
libName = uniq('lib');
|
||||
runCLI(
|
||||
`generate @nx/react-native:lib ${libName} --buildable --no-interactive --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
componentName = uniq('Component');
|
||||
runCLI(
|
||||
`generate @nx/react-native:component ${libName}/src/lib/${componentName}/${componentName} --export --no-interactive`
|
||||
);
|
||||
updateFile(`${appName}/src/app/App.tsx`, (content) => {
|
||||
let updated = `// eslint-disable-next-line @typescript-eslint/no-unused-vars\nimport {${componentName}} from '${proj}/${libName}';\n${content}`;
|
||||
return updated;
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => cleanupProject());
|
||||
|
||||
it('should test and lint', async () => {
|
||||
expect(() => runCLI(`test ${appName}`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${appName}`)).not.toThrow();
|
||||
});
|
||||
|
||||
it('should bundle the app', async () => {
|
||||
expect(() =>
|
||||
runCLI(
|
||||
@ -104,12 +125,38 @@ describe('@nx/react-native', () => {
|
||||
|
||||
it('should create storybook with application', async () => {
|
||||
runCLI(
|
||||
`generate @nx/react:storybook-configuration ${appName} --generateStories --no-interactive`
|
||||
`generate @nx/react-native:storybook-configuration ${appName} --generateStories --no-interactive`
|
||||
);
|
||||
checkFilesExist(
|
||||
`${appName}/.storybook/main.ts`,
|
||||
`${appName}/src/app/App.stories.tsx`
|
||||
);
|
||||
|
||||
runCLI(`build-storybook ${appName}`);
|
||||
checkFilesExist(`${appName}/storybook-static/index.html`);
|
||||
});
|
||||
|
||||
it('should build publishable library', async () => {
|
||||
expect(() => {
|
||||
runCLI(`build ${libName}`);
|
||||
checkFilesExist(
|
||||
`dist/${libName}/index.esm.js`,
|
||||
`dist/${libName}/src/index.d.ts`
|
||||
);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('should create storybook with library', async () => {
|
||||
runCLI(
|
||||
`generate @nx/react-native:storybook-configuration ${libName} --generateStories --no-interactive`
|
||||
);
|
||||
checkFilesExist(
|
||||
`${libName}/.storybook/main.ts`,
|
||||
`${libName}/src/lib/${componentName}/${componentName}.stories.tsx`
|
||||
);
|
||||
|
||||
runCLI(`build-storybook ${libName}`);
|
||||
checkFilesExist(`${libName}/storybook-static/index.html`);
|
||||
});
|
||||
|
||||
it('should run build with vite bundler and e2e with playwright', async () => {
|
||||
@ -137,5 +184,7 @@ describe('@nx/react-native', () => {
|
||||
`apps/${appName2}/.storybook/main.ts`,
|
||||
`apps/${appName2}/src/app/App.stories.tsx`
|
||||
);
|
||||
runCLI(`build-storybook ${appName2}`);
|
||||
checkFilesExist(`apps/${appName2}/storybook-static/index.html`);
|
||||
});
|
||||
});
|
||||
|
||||
@ -0,0 +1,61 @@
|
||||
This generator will set up Storybook for your **React Native** project.
|
||||
|
||||
```bash
|
||||
nx g @nx/react-native:storybook-configuration project-name
|
||||
```
|
||||
|
||||
When running this generator, you will be prompted to provide the following:
|
||||
|
||||
- The `name` of the project you want to generate the configuration for.
|
||||
- Whether you want to set up [Storybook interaction tests](https://storybook.js.org/docs/react/writing-tests/interaction-testing) (`interactionTests`). If you choose `yes`, a `play` function will be added to your stories, and all the necessary dependencies will be installed. Also, a `test-storybook` target will be generated in your project's `project.json`, with a command to invoke the [Storybook `test-runner`](https://storybook.js.org/docs/react/writing-tests/test-runner). You can read more about this in the [Nx Storybook interaction tests documentation page](/recipes/storybook/storybook-interaction-tests#setup-storybook-interaction-tests)..
|
||||
- Whether you want to `generateStories` for the components in your project. If you choose `yes`, a `.stories.ts` file will be generated next to each of your components in your project.
|
||||
|
||||
You must provide a `name` for the generator to work.
|
||||
|
||||
By default, this generator will also set up [Storybook interaction tests](https://storybook.js.org/docs/react/writing-tests/interaction-testing). If you don't want to set up Storybook interaction tests, you can pass the `--interactionTests=false` option, but it's not recommended.
|
||||
|
||||
There are a number of other options available. Let's take a look at some examples.
|
||||
|
||||
## Examples
|
||||
|
||||
### Generate Storybook configuration
|
||||
|
||||
```bash
|
||||
nx g @nx/react-native:storybook-configuration ui
|
||||
```
|
||||
|
||||
This will generate Storybook configuration for the `ui` project using TypeScript for the Storybook configuration files (the files inside the `.storybook` directory, eg. `.storybook/main.ts`).
|
||||
|
||||
### Ignore certain paths when generating stories
|
||||
|
||||
```bash
|
||||
nx g @nx/react-native:storybook-configuration ui --generateStories=true --ignorePaths=libs/ui/src/not-stories/**,**/**/src/**/*.other.*,apps/my-app/**/*.something.ts
|
||||
```
|
||||
|
||||
This will generate a Storybook configuration for the `ui` project and generate stories for all components in the `libs/ui/src/lib` directory, except for the ones in the `libs/ui/src/not-stories` directory, and the ones in the `apps/my-app` directory that end with `.something.ts`, and also for components that their file name is of the pattern `*.other.*`.
|
||||
|
||||
This is useful if you have a project that contains components that are not meant to be used in isolation, but rather as part of a larger component.
|
||||
|
||||
By default, Nx will ignore the following paths:
|
||||
|
||||
```text
|
||||
*.stories.ts, *.stories.tsx, *.stories.js, *.stories.jsx, *.stories.mdx
|
||||
```
|
||||
|
||||
but you can change this behaviour easily, as explained above.
|
||||
|
||||
### Generate stories using JavaScript instead of TypeScript
|
||||
|
||||
```bash
|
||||
nx g @nx/react-native:storybook-configuration ui --generateStories=true --js=true
|
||||
```
|
||||
|
||||
This will generate stories for all the components in the `ui` project using JavaScript instead of TypeScript. So, you will have `.stories.js` files next to your components.
|
||||
|
||||
### Generate Storybook configuration using JavaScript
|
||||
|
||||
```bash
|
||||
nx g @nx/react-native:storybook-configuration ui --tsConfiguration=false
|
||||
```
|
||||
|
||||
By default, our generator generates TypeScript Storybook configuration files. You can choose to use JavaScript for the Storybook configuration files of your project (the files inside the `.storybook` directory, eg. `.storybook/main.js`).
|
||||
@ -59,7 +59,10 @@ export const createNodesV2: CreateNodesV2<ReactNativePluginOptions> = [
|
||||
'**/app.{json,config.js,config.ts}',
|
||||
async (configFiles, options, context) => {
|
||||
const optionsHash = hashObject(options);
|
||||
const cachePath = join(workspaceDataDirectory, `expo-${optionsHash}.hash`);
|
||||
const cachePath = join(
|
||||
workspaceDataDirectory,
|
||||
`react-native-${optionsHash}.hash`
|
||||
);
|
||||
const targetsCache = readTargetsCache(cachePath);
|
||||
|
||||
try {
|
||||
|
||||
@ -93,7 +93,7 @@
|
||||
"type": "string",
|
||||
"enum": ["vite", "webpack"],
|
||||
"x-prompt": "Which bundler do you want to use to build the application?",
|
||||
"default": "webpack",
|
||||
"default": "vite",
|
||||
"x-priority": "important"
|
||||
}
|
||||
},
|
||||
|
||||
@ -75,5 +75,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": ["project"]
|
||||
"required": ["project"],
|
||||
"examplesFile": "../../../docs/storybook-configuration-examples.md"
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ const rollupPlugin = (matchers: RegExp[]) => ({
|
||||
|
||||
export default defineConfig({
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/<%= fileName %>',
|
||||
cacheDir: '<%= offsetFromRoot %>node_modules/.vite/<%= projectRoot %>',
|
||||
define: {
|
||||
global: 'window',
|
||||
},
|
||||
@ -43,7 +43,7 @@ export default defineConfig({
|
||||
build: {
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: { transformMixedEsModules: true },
|
||||
outDir: '../../dist/apps/<%= fileName %>/web',
|
||||
outDir: '<%= offsetFromRoot %>dist/<%= projectRoot %>/web',
|
||||
rollupOptions: {
|
||||
plugins: [rollupPlugin([/react-native-vector-icons/])],
|
||||
},
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
"type": "string",
|
||||
"enum": ["vite", "webpack"],
|
||||
"x-prompt": "Which bundler do you want to use to build the application?",
|
||||
"default": "webpack",
|
||||
"default": "vite",
|
||||
"x-priority": "important"
|
||||
}
|
||||
},
|
||||
|
||||
@ -37,6 +37,7 @@ export async function addLinting(host: Tree, options: NormalizedSchema) {
|
||||
tsConfigPaths: options.tsConfigPaths,
|
||||
skipFormat: true,
|
||||
skipPackageJson: options.skipPackageJson,
|
||||
setParserOptionsProject: options.setParserOptionsProject,
|
||||
addPlugin: options.addPlugin,
|
||||
});
|
||||
|
||||
@ -69,7 +70,7 @@ export async function addLinting(host: Tree, options: NormalizedSchema) {
|
||||
}
|
||||
|
||||
if (!options.skipPackageJson) {
|
||||
const installTask = await addDependenciesToPackageJson(
|
||||
const installTask = addDependenciesToPackageJson(
|
||||
host,
|
||||
extraEslintDependencies.dependencies,
|
||||
extraEslintDependencies.devDependencies
|
||||
|
||||
@ -25,9 +25,9 @@ import {
|
||||
createProjectStorybookDir,
|
||||
createStorybookTsconfigFile,
|
||||
editTsconfigBaseJson,
|
||||
findMetroConfig,
|
||||
findNextConfig,
|
||||
findViteConfig,
|
||||
isUsingReactNative,
|
||||
projectIsRootProjectInStandaloneWorkspace,
|
||||
updateLintConfig,
|
||||
} from './lib/util-functions';
|
||||
@ -82,7 +82,6 @@ export async function configurationGeneratorInternal(
|
||||
const viteConfigFilePath = viteConfig?.fullConfigPath;
|
||||
const viteConfigFileName = viteConfig?.viteConfigFileName;
|
||||
const nextConfigFilePath = findNextConfig(tree, root);
|
||||
const metroConfigFilePath = findMetroConfig(tree, root);
|
||||
|
||||
if (viteConfigFilePath) {
|
||||
if (schema.uiFramework === '@storybook/react-webpack5') {
|
||||
@ -133,7 +132,7 @@ export async function configurationGeneratorInternal(
|
||||
|
||||
const usesVite =
|
||||
!!viteConfigFilePath || schema.uiFramework?.endsWith('-vite');
|
||||
const useReactNative = !!metroConfigFilePath;
|
||||
const usesReactNative = isUsingReactNative(schema.project);
|
||||
|
||||
createProjectStorybookDir(
|
||||
tree,
|
||||
@ -152,7 +151,7 @@ export async function configurationGeneratorInternal(
|
||||
viteConfigFilePath,
|
||||
hasPlugin,
|
||||
viteConfigFileName,
|
||||
useReactNative
|
||||
usesReactNative
|
||||
);
|
||||
|
||||
if (schema.uiFramework !== '@storybook/angular') {
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
joinPathFragments,
|
||||
logger,
|
||||
offsetFromRoot,
|
||||
readCachedProjectGraph,
|
||||
readJson,
|
||||
readNxJson,
|
||||
readProjectConfiguration,
|
||||
@ -577,7 +578,7 @@ export function createProjectStorybookDir(
|
||||
viteConfigFilePath?: string,
|
||||
hasPlugin?: boolean,
|
||||
viteConfigFileName?: string,
|
||||
useReactNative?: boolean
|
||||
usesReactNative?: boolean
|
||||
) {
|
||||
let projectDirectory =
|
||||
projectType === 'application'
|
||||
@ -622,7 +623,7 @@ export function createProjectStorybookDir(
|
||||
viteConfigFilePath,
|
||||
hasPlugin,
|
||||
viteConfigFileName,
|
||||
useReactNative,
|
||||
usesReactNative,
|
||||
});
|
||||
|
||||
if (js) {
|
||||
@ -739,13 +740,14 @@ export function findNextConfig(
|
||||
}
|
||||
}
|
||||
|
||||
export function findMetroConfig(
|
||||
tree: Tree,
|
||||
projectRoot: string
|
||||
): string | undefined {
|
||||
const nextConfigPath = joinPathFragments(projectRoot, `metro.config.js`);
|
||||
if (tree.exists(nextConfigPath)) {
|
||||
return nextConfigPath;
|
||||
export function isUsingReactNative(projectName: string): boolean {
|
||||
try {
|
||||
const projectGraph = readCachedProjectGraph();
|
||||
return projectGraph?.dependencies?.[projectName]?.some(
|
||||
(dep) => dep.target === 'npm:react-native'
|
||||
);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ const config: StorybookConfig = {
|
||||
<% } %>
|
||||
},
|
||||
},
|
||||
<% if (useReactNative && uiFramework === '@storybook/react-webpack5') { %>webpackFinal: async (config) => {
|
||||
<% if (usesReactNative && uiFramework === '@storybook/react-webpack5') { %>webpackFinal: async (config) => {
|
||||
if (config.resolve) {
|
||||
config.resolve.alias = {
|
||||
...config.resolve.alias,
|
||||
@ -43,6 +43,21 @@ const config: StorybookConfig = {
|
||||
},<% } %><% if (usesVite && !viteConfigFilePath) { %>
|
||||
viteFinal: async (config) =>
|
||||
mergeConfig(config, {
|
||||
<% if (usesReactNative) { %>define: {
|
||||
global: 'window',
|
||||
},
|
||||
resolve: {
|
||||
extensions: [
|
||||
'.web.tsx',
|
||||
'.web.ts',
|
||||
'.web.jsx',
|
||||
'.web.js',
|
||||
...(config.resolve?.extensions ?? []),
|
||||
],
|
||||
alias: {
|
||||
'react-native': 'react-native-web',
|
||||
},
|
||||
},<% } %>
|
||||
plugins: [<% if(uiFramework === '@storybook/vue3-vite') { %>vue(), <% } %><% if(uiFramework === '@storybook/react-vite') { %>react(), <% } %>nxViteTsPaths()],
|
||||
}),
|
||||
<% } %>
|
||||
|
||||
@ -25,7 +25,7 @@ const config = {
|
||||
<% } %>
|
||||
},
|
||||
},
|
||||
<% if (useReactNative && uiFramework === '@storybook/react-webpack5') { %>webpackFinal: async (config) => {
|
||||
<% if (usesReactNative && uiFramework === '@storybook/react-webpack5') { %>webpackFinal: async (config) => {
|
||||
if (config.resolve) {
|
||||
config.resolve.alias = {
|
||||
...config.resolve.alias,
|
||||
@ -43,6 +43,21 @@ const config = {
|
||||
},<% } %><% if (usesVite && !viteConfigFilePath) { %>
|
||||
viteFinal: async (config) =>
|
||||
mergeConfig(config, {
|
||||
<% if (usesReactNative) { %>define: {
|
||||
global: 'window',
|
||||
},
|
||||
resolve: {
|
||||
extensions: [
|
||||
'.web.tsx',
|
||||
'.web.ts',
|
||||
'.web.jsx',
|
||||
'.web.js',
|
||||
...(config.resolve.extensions ?? []),
|
||||
],
|
||||
alias: {
|
||||
'react-native': 'react-native-web',
|
||||
},
|
||||
},<% } %>
|
||||
plugins: [<% if(uiFramework === '@storybook/vue3-vite') { %>vue(), <% } %>nxViteTsPaths()],
|
||||
}),
|
||||
<% } %>
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
TargetConfiguration,
|
||||
Tree,
|
||||
readNxJson,
|
||||
updateNxJson,
|
||||
} from '@nx/devkit';
|
||||
import { TargetConfiguration, Tree } from '@nx/devkit';
|
||||
import { CompilerOptions } from 'typescript';
|
||||
import { statSync } from 'fs';
|
||||
import { findNodes } from '@nx/js';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user