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

CLI: Add support for Nuxt to project init #26884

Open
wants to merge 5 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion code/lib/cli/package.json
Expand Up @@ -53,7 +53,8 @@
],
"scripts": {
"check": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/check.ts",
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts"
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts",
"sb": "node ./bin/index.js"
},
"dependencies": {
"@babel/core": "^7.24.4",
Expand Down
32 changes: 22 additions & 10 deletions code/lib/cli/src/detect.test.ts
Expand Up @@ -41,6 +41,28 @@ const MOCK_FRAMEWORK_FILES: {
},
},
},
{
name: ProjectType.NUXT,
files: {
'package.json': {
dependencies: {
nuxt: '^3.11.2',
},
},
},
},
{
name: ProjectType.NUXT,
files: {
'package.json': {
dependencies: {
// Nuxt projects may have Vue 3 as an explicit dependency
nuxt: '^3.11.2',
vue: '^3.0.0',
},
},
},
},
{
name: ProjectType.VUE3,
files: {
Expand Down Expand Up @@ -433,16 +455,6 @@ describe('Detect', () => {
expect(result).toBe(ProjectType.UNDETECTED);
});

// TODO(blaine): Remove once Nuxt3 is supported
it(`UNSUPPORTED for Nuxt framework above version 3.0.0`, () => {
const result = detectFrameworkPreset({
dependencies: {
nuxt: '3.0.0',
},
});
expect(result).toBe(ProjectType.UNSUPPORTED);
});

// TODO: The mocking in this test causes tests after it to fail
it('REACT_SCRIPTS for custom react scripts config', () => {
const forkedReactScriptsConfig = {
Expand Down
15 changes: 14 additions & 1 deletion code/lib/cli/src/detect.ts
Expand Up @@ -120,7 +120,11 @@ export async function detectBuilder(packageManager: JsPackageManager, projectTyp
}

// REWORK
if (webpackConfig || (dependencies['webpack'] && dependencies['vite'] !== undefined)) {
if (
webpackConfig ||
((dependencies['webpack'] || dependencies['@nuxt/webpack-builder']) &&
dependencies['vite'] !== undefined)
) {
commandLog('Detected webpack project. Setting builder to webpack')();
return CoreBuilder.Webpack5;
}
Expand All @@ -133,6 +137,8 @@ export async function detectBuilder(packageManager: JsPackageManager, projectTyp
case ProjectType.NEXTJS:
case ProjectType.EMBER:
return CoreBuilder.Webpack5;
case ProjectType.NUXT:
return CoreBuilder.Vite;
default:
const { builder } = await prompts(
{
Expand Down Expand Up @@ -202,6 +208,13 @@ export async function detectLanguage(packageManager: JsPackageManager) {
} else if (semver.lt(typescriptVersion, '3.8.0')) {
logger.warn('Detected TypeScript < 3.8, populating with JavaScript examples');
}
} else {
// No direct dependency on TypeScript, but could be a transitive dependency
// This is eg the case for Nuxt projects, which support a recent version of TypeScript
// Check for tsconfig.json (https://www.typescriptlang.org/docs/handbook/tsconfig-json.html)
if (fs.existsSync('tsconfig.json')) {
language = SupportedLanguage.TYPESCRIPT_4_9;
}
}

return language;
Expand Down
32 changes: 32 additions & 0 deletions code/lib/cli/src/generators/NUXT/index.ts
@@ -0,0 +1,32 @@
import { baseGenerator } from '../baseGenerator';
import type { Generator } from '../types';

const generator: Generator = async (packageManager, npmOptions, options) => {
await baseGenerator(
packageManager,
npmOptions,
options,
'nuxt',
{
extraPackages: async ({ builder }) => {
return ['@nuxtjs/storybook'];
},
installStorybookPackage: false,
kasperpeulen marked this conversation as resolved.
Show resolved Hide resolved
installFrameworkPackages: false,
componentsDestinationPath: './components',
extraMain: {
stories: ['../components/**/*.mdx', '../components/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
},
},
'nuxt'
);
// Add nuxtjs/storybook to nuxt.config.js
await packageManager.runPackageCommand('nuxi', [
'module',
'add',
'@nuxtjs/storybook',
'--skipInstall',
]);
};

export default generator;
28 changes: 19 additions & 9 deletions code/lib/cli/src/generators/baseGenerator.ts
Expand Up @@ -23,13 +23,16 @@ const defaultOptions: FrameworkOptions = {
staticDir: undefined,
addScripts: true,
addMainFile: true,
addPreviewFile: true,
kasperpeulen marked this conversation as resolved.
Show resolved Hide resolved
addComponents: true,
webpackCompiler: () => undefined,
extraMain: undefined,
framework: undefined,
extensions: undefined,
componentsDestinationPath: undefined,
storybookConfigFolder: '.storybook',
installStorybookPackage: true,
installFrameworkPackages: true,
};

const getBuilderDetails = (builder: string) => {
Expand Down Expand Up @@ -202,12 +205,15 @@ export async function baseGenerator(
staticDir,
addScripts,
addMainFile,
addPreviewFile,
addComponents,
extraMain,
extensions,
storybookConfigFolder,
componentsDestinationPath,
webpackCompiler,
installStorybookPackage,
installFrameworkPackages,
} = {
...defaultOptions,
...options,
Expand Down Expand Up @@ -279,9 +285,9 @@ export async function baseGenerator(
: extraPackages;

const allPackages = [
'storybook',
installStorybookPackage ? 'storybook' : undefined,
getExternalFramework(rendererId) ? undefined : `@storybook/${rendererId}`,
...frameworkPackages,
...(installFrameworkPackages ? frameworkPackages : []),
...addonPackages,
...(extraPackagesToInstall || []),
].filter(Boolean);
Expand Down Expand Up @@ -323,7 +329,9 @@ export async function baseGenerator(
addDependenciesSpinner.succeed();
}

await fse.ensureDir(`./${storybookConfigFolder}`);
if (addMainFile || addPreviewFile) {
await fse.ensureDir(`./${storybookConfigFolder}`);
}

if (addMainFile) {
const prefixes = shouldApplyRequireWrapperOnPackageNames
Expand Down Expand Up @@ -371,12 +379,14 @@ export async function baseGenerator(
});
}

await configurePreview({
frameworkPreviewParts,
storybookConfigFolder: storybookConfigFolder as string,
language,
rendererId,
});
if (addPreviewFile) {
await configurePreview({
frameworkPreviewParts,
storybookConfigFolder: storybookConfigFolder as string,
language,
rendererId,
});
}

if (addScripts) {
await packageManager.addStorybookCommandInScripts({
Expand Down
3 changes: 3 additions & 0 deletions code/lib/cli/src/generators/types.ts
Expand Up @@ -22,13 +22,16 @@ export interface FrameworkOptions {
staticDir?: string;
addScripts?: boolean;
addMainFile?: boolean;
addPreviewFile?: boolean;
addComponents?: boolean;
webpackCompiler?: ({ builder }: { builder: Builder }) => 'babel' | 'swc' | undefined;
extraMain?: any;
extensions?: string[];
framework?: Record<string, any>;
storybookConfigFolder?: string;
componentsDestinationPath?: string;
installStorybookPackage?: boolean;
installFrameworkPackages?: boolean;
}

export type Generator<T = void> = (
Expand Down
1 change: 1 addition & 0 deletions code/lib/cli/src/helpers.ts
Expand Up @@ -143,6 +143,7 @@ export const frameworkToDefaultBuilder: Record<SupportedFrameworks, CoreBuilder>
'html-vite': CoreBuilder.Vite,
'html-webpack5': CoreBuilder.Webpack5,
nextjs: CoreBuilder.Webpack5,
nuxt: CoreBuilder.Vite,
'preact-vite': CoreBuilder.Vite,
'preact-webpack5': CoreBuilder.Webpack5,
qwik: CoreBuilder.Vite,
Expand Down
6 changes: 6 additions & 0 deletions code/lib/cli/src/initiate.ts
Expand Up @@ -28,6 +28,7 @@ import reactNativeGenerator from './generators/REACT_NATIVE';
import reactScriptsGenerator from './generators/REACT_SCRIPTS';
import nextjsGenerator from './generators/NEXTJS';
import vue3Generator from './generators/VUE3';
import nuxtGenerator from './generators/NUXT';
import webpackReactGenerator from './generators/WEBPACK_REACT';
import htmlGenerator from './generators/HTML';
import webComponentsGenerator from './generators/WEB-COMPONENTS';
Expand Down Expand Up @@ -109,6 +110,11 @@ const installStorybook = async <Project extends ProjectType>(
commandLog('Adding Storybook support to your "Vue 3" app')
);

case ProjectType.NUXT:
return nuxtGenerator(packageManager, npmOptions, generatorOptions).then(
commandLog('Adding Storybook support to your "Nuxt" app')
);

case ProjectType.ANGULAR:
commandLog('Adding Storybook support to your "Angular" app');
return angularGenerator(packageManager, npmOptions, generatorOptions, options);
Expand Down
14 changes: 10 additions & 4 deletions code/lib/cli/src/project_types.ts
Expand Up @@ -23,6 +23,7 @@ export type ExternalFramework = {
export const externalFrameworks: ExternalFramework[] = [
{ name: 'qwik', packageName: 'storybook-framework-qwik' },
{ name: 'solid', frameworks: ['storybook-solidjs-vite'], renderer: 'storybook-solidjs' },
{ name: 'nuxt', packageName: '@storybook-vue/nuxt' },
];

/**
Expand Down Expand Up @@ -52,6 +53,7 @@ export enum ProjectType {
WEBPACK_REACT = 'WEBPACK_REACT',
NEXTJS = 'NEXTJS',
VUE3 = 'VUE3',
NUXT = 'NUXT',
ANGULAR = 'ANGULAR',
EMBER = 'EMBER',
WEB_COMPONENTS = 'WEB_COMPONENTS',
Expand Down Expand Up @@ -117,6 +119,13 @@ export type TemplateConfiguration = {
* therefore WEBPACK_REACT has to come first, as it's more specific.
*/
export const supportedTemplates: TemplateConfiguration[] = [
{
preset: ProjectType.NUXT,
dependencies: ['nuxt'],
matcherFunction: ({ dependencies }) => {
return dependencies?.every(Boolean) ?? true;
},
},
{
preset: ProjectType.VUE3,
dependencies: {
Expand Down Expand Up @@ -238,10 +247,7 @@ export const supportedTemplates: TemplateConfiguration[] = [
// users an "Unsupported framework" message
export const unsupportedTemplate: TemplateConfiguration = {
preset: ProjectType.UNSUPPORTED,
dependencies: {
// TODO(blaine): Remove when we support Nuxt 3
nuxt: (versionRange) => eqMajor(versionRange, 3),
},
dependencies: {},
matcherFunction: ({ dependencies }) => {
return dependencies?.some(Boolean) ?? false;
},
Expand Down
1 change: 1 addition & 0 deletions code/lib/core-common/src/utils/framework-to-renderer.ts
Expand Up @@ -22,6 +22,7 @@ export const frameworkToRenderer: Record<
sveltekit: 'svelte',
'vue3-vite': 'vue3',
'vue3-webpack5': 'vue3',
nuxt: 'vue3',
'web-components-vite': 'web-components',
'web-components-webpack5': 'web-components',
// renderers
Expand Down
2 changes: 1 addition & 1 deletion code/lib/types/scripts/generate-available-frameworks.js
Expand Up @@ -6,7 +6,7 @@ import path from 'node:path';
import prettier from 'prettier';
import dedent from 'ts-dedent';

const thirdPartyFrameworks = ['qwik', 'solid'];
const thirdPartyFrameworks = ['qwik', 'solid', 'nuxt'];

const run = async () => {
const frameworks = await readdir(path.join(__dirname, '..', '..', '..', 'frameworks'));
Expand Down
3 changes: 2 additions & 1 deletion code/lib/types/src/modules/frameworks.ts
Expand Up @@ -19,4 +19,5 @@ export type SupportedFrameworks =
| 'web-components-vite'
| 'web-components-webpack5'
| 'qwik'
| 'solid';
| 'solid'
| 'nuxt';
3 changes: 2 additions & 1 deletion code/lib/types/src/modules/renderers.ts
Expand Up @@ -11,4 +11,5 @@ export type SupportedRenderers =
| 'html'
| 'web-components'
| 'server'
| 'solid';
| 'solid'
| 'nuxt';