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

Build: sandbox for pnp #20998

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .yarnrc.yml

This file was deleted.

4 changes: 2 additions & 2 deletions code/addons/essentials/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import path from 'path';
import path, { dirname, join } from 'path';
import { logger } from '@storybook/node-logger';
import { serverRequire } from '@storybook/core-common';

Expand Down Expand Up @@ -52,6 +52,6 @@ export function addons(options: PresetOptions) {
.filter((addon) => !checkInstalled(addon, main))
.map((addon) => {
// We point to the re-export from addon-essentials to support yarn pnp and pnpm.
return `@storybook/addon-essentials/${addon}`;
return join(dirname(require.resolve(`@storybook/addon-essentials/package.json`)), addon);
});
}
20 changes: 14 additions & 6 deletions code/lib/cli/src/dirs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { safeResolveFrom } from '@storybook/core-common';
import { dirname } from 'path';
import dedent from 'ts-dedent';
import type { SupportedFrameworks, SupportedRenderers } from './project_types';
import { externalFrameworks } from './project_types';

Expand All @@ -8,10 +10,16 @@ export function getCliDir() {

export function getRendererDir(renderer: SupportedFrameworks | SupportedRenderers) {
const externalFramework = externalFrameworks.find((framework) => framework.name === renderer);
const frameworkPackageName = externalFramework?.packageName ?? `@storybook/${renderer}`;
return dirname(
require.resolve(`${frameworkPackageName}/package.json`, {
paths: [process.cwd()],
})
);
const isExternal = externalFramework?.packageName;
const frameworkPackageName = isExternal ?? `@storybook/${renderer}`;

const found = safeResolveFrom(process.cwd(), `${frameworkPackageName}/package.json`);

if (found) {
return dirname(found);
}

throw new Error(dedent`
Unable to find ${frameworkPackageName}, are you sure it's installed?
`);
}
1 change: 0 additions & 1 deletion code/lib/cli/src/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ command('init')
.option('-s --skip-install', 'Skip installing deps')
.option('--package-manager <npm|pnpm|yarn1|yarn2>', 'Force package manager for installing deps')
.option('-N --use-npm', 'Use npm to install deps (deprecated)')
.option('--use-pnp', 'Enable pnp mode for Yarn 2+')
.option('-p --parser <babel | babylon | flow | ts | tsx>', 'jscodeshift parser')
.option('-t --type <type>', 'Add Storybook for a specific project type')
.option('-y --yes', 'Answer yes to all prompts')
Expand Down
16 changes: 6 additions & 10 deletions code/lib/cli/src/generators/baseGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,9 @@ const getFrameworkPackage = (framework: string, renderer: string, builder: strin
return framework ? `@storybook/${framework}` : `@storybook/${renderer}-${builder}`;
};

const wrapForPnp = (packageName: string) =>
`%%path.dirname(require.resolve(path.join('${packageName}', 'package.json')))%%`;

const getFrameworkDetails = (
renderer: SupportedRenderers,
builder: Builder,
pnp: boolean,
framework?: SupportedFrameworks
): {
type: 'framework' | 'renderer';
Expand All @@ -74,13 +70,13 @@ const getFrameworkDetails = (
} => {
const frameworkPackage = getFrameworkPackage(framework, renderer, builder);

const frameworkPackagePath = pnp ? wrapForPnp(frameworkPackage) : frameworkPackage;
const frameworkPackagePath = frameworkPackage;

const rendererPackage = `@storybook/${renderer}`;
const rendererPackagePath = pnp ? wrapForPnp(rendererPackage) : rendererPackage;
const rendererPackagePath = rendererPackage;

const builderPackage = getBuilderDetails(builder);
const builderPackagePath = pnp ? wrapForPnp(builderPackage) : builderPackage;
const builderPackagePath = builderPackage;

const isExternalFramework = !!getExternalFramework(frameworkPackage);
const isKnownFramework =
Expand Down Expand Up @@ -122,7 +118,7 @@ const hasFrameworkTemplates = (framework?: SupportedFrameworks) =>
export async function baseGenerator(
packageManager: JsPackageManager,
npmOptions: NpmOptions,
{ language, builder = CoreBuilder.Webpack5, pnp, frameworkPreviewParts }: GeneratorOptions,
{ language, builder = CoreBuilder.Webpack5, frameworkPreviewParts }: GeneratorOptions,
renderer: SupportedRenderers,
options: FrameworkOptions = defaultOptions,
framework?: SupportedFrameworks
Expand Down Expand Up @@ -151,7 +147,7 @@ export async function baseGenerator(
rendererId,
framework: frameworkInclude,
builder: builderInclude,
} = getFrameworkDetails(renderer, builder, pnp, framework);
} = getFrameworkDetails(renderer, builder, framework);

// added to main.js
const addons = [
Expand Down Expand Up @@ -223,7 +219,7 @@ export async function baseGenerator(
framework: { name: frameworkInclude, options: options.framework || {} },
storybookConfigFolder,
docs: { autodocs: 'tag' },
addons: pnp ? addons.map(wrapForPnp) : addons,
addons,
extensions,
language,
...(staticDir ? { staticDirs: [path.join('..', staticDir)] } : null),
Expand Down
1 change: 0 additions & 1 deletion code/lib/cli/src/generators/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export type GeneratorOptions = {
language: SupportedLanguage;
builder: Builder;
linkable: boolean;
pnp: boolean;
frameworkPreviewParts?: FrameworkPreviewParts;
};

Expand Down
3 changes: 1 addition & 2 deletions code/lib/cli/src/initiate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { telemetry } from '@storybook/telemetry';
import { withTelemetry } from '@storybook/core-server';

import { installableProjectTypes, ProjectType } from './project_types';
import { detect, isStorybookInstalled, detectLanguage, detectBuilder } from './detect';
import { detect, isStorybookInstalled, detectLanguage, detectBuilder, detectPnp } from './detect';
import { commandLog, codeLog, paddedLog } from './helpers';
import angularGenerator from './generators/ANGULAR';
import aureliaGenerator from './generators/AURELIA';
Expand Down Expand Up @@ -61,7 +61,6 @@ const installStorybook = <Project extends ProjectType>(
language,
builder: options.builder || detectBuilder(packageManager),
linkable: !!options.linkable,
pnp: options.usePnp,
};

const runGenerator: () => Promise<any> = async () => {
Expand Down
40 changes: 18 additions & 22 deletions code/lib/cli/src/repro-generators/scripts.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import path from 'path';
import { readJSON, writeJSON, outputFile } from 'fs-extra';
import { readJSON, writeJSON, outputFile, pathExists } from 'fs-extra';
import type { ExecOptions } from 'shelljs';
import shell from 'shelljs';
import chalk from 'chalk';
import { command } from 'execa';
import { command as execaCommand } from 'execa';
import { cra, cra_typescript } from './configs';
import storybookVersions from '../versions';

Expand Down Expand Up @@ -51,7 +51,6 @@ export interface Options extends Parameters {
}

export const exec = async (
// eslint-disable-next-line @typescript-eslint/no-shadow
command: string,
options: ExecOptions = {},
{
Expand Down Expand Up @@ -106,11 +105,9 @@ const addLocalPackageResolutions = async ({ cwd }: Options) => {
const packageJsonPath = path.join(cwd, 'package.json');
const packageJson = await readJSON(packageJsonPath);
const workspaceDir = path.join(__dirname, '..', '..', '..', '..', '..');
const { stdout } = await command('yarn workspaces list --json', { cwd: workspaceDir });
const { stdout } = await execaCommand('yarn workspaces list --json', { cwd: workspaceDir });

console.log({ stdout, workspaceDir });
const workspaces = JSON.parse(`[${stdout.split('\n').join(',')}]`);
console.log({ workspaces });

packageJson.resolutions = Object.keys(storybookVersions).reduce((acc, key) => {
return {
Expand All @@ -122,12 +119,17 @@ const addLocalPackageResolutions = async ({ cwd }: Options) => {
};

const installYarn2 = async ({ cwd, pnp, name }: Options) => {
// eslint-disable-next-line @typescript-eslint/no-shadow
const command = [
`yarn set version berry`,
`yarn config set enableGlobalCache true`,
`yarn config set nodeLinker ${pnp ? 'pnp' : 'node-modules'}`,
];
const { stdout } = await execaCommand(`yarn config --json`, { cwd });
const pnpApiExists = await pathExists(path.join(cwd, '.pnp.cjs'));

const data = JSON.parse(`[${stdout.replaceAll('\n', ',')}]`);
const nodeLinkerConfig = data.find((d: any) => d.key === 'nodeLinker');

const command = [`yarn set version berry`, `yarn config set enableGlobalCache true`];

if (!nodeLinkerConfig.source && !pnpApiExists) {
command.push(`yarn config set nodeLinker node-modules`);
}

// FIXME: Some dependencies used by CRA aren't listed in its package.json
// Next line is a hack to remove as soon as CRA will have added these missing deps
Expand All @@ -146,7 +148,6 @@ const installYarn2 = async ({ cwd, pnp, name }: Options) => {
};

const configureYarn2ForE2E = async ({ cwd }: Options) => {
// eslint-disable-next-line @typescript-eslint/no-shadow
const command = [
// ⚠️ Need to set registry because Yarn 2 is not using the conf of Yarn 1 (URL is hardcoded in CircleCI config.yml)
`yarn config set npmScopes --json '{ "storybook": { "npmRegistryServer": "http://localhost:6001/" } }'`,
Expand All @@ -168,7 +169,6 @@ const configureYarn2ForE2E = async ({ cwd }: Options) => {
};

const generate = async ({ cwd, name, appName, version, generator, envs }: Options) => {
// eslint-disable-next-line @typescript-eslint/no-shadow
const command = generator.replace(/{{appName}}/g, appName).replace(/{{version}}/g, version);

await exec(
Expand All @@ -191,7 +191,7 @@ const addAdditionalFiles = async ({ additionalFiles, cwd }: Options) => {
);
};

const initStorybook = async ({ cwd, autoDetect = true, name, e2e, pnp }: Options) => {
const initStorybook = async ({ cwd, autoDetect = true, name, e2e }: Options) => {
const flags = ['--yes'];

if (!autoDetect) {
Expand All @@ -200,14 +200,10 @@ const initStorybook = async ({ cwd, autoDetect = true, name, e2e, pnp }: Options
if (e2e) {
flags.push('--linkable');
}
if (pnp) {
flags.push('--use-pnp');
}

// This is bundled into a single javascript file.
const sbCLICommand = `node ${__filename}`;

// eslint-disable-next-line @typescript-eslint/no-shadow
const command = `${sbCLICommand} init ${flags.join(' ')}`;

await exec(
Expand All @@ -224,7 +220,6 @@ const addRequiredDeps = async ({ cwd, additionalDeps }: Options) => {
// Remove any lockfile generated without Yarn 2
shell.rm('-f', path.join(cwd, 'package-lock.json'), path.join(cwd, 'yarn.lock'));

// eslint-disable-next-line @typescript-eslint/no-shadow
const command =
additionalDeps && additionalDeps.length > 0
? `yarn add -D ${additionalDeps.join(' ')}`
Expand Down Expand Up @@ -286,7 +281,7 @@ const registryUrlYarn = (url: string) => {
export const createAndInit = async (
cwd: string,
{ name, version, ...rest }: Parameters,
{ e2e, pnp, local, registry }: Configuration
{ e2e, local, registry }: Configuration
) => {
const options: Options = {
name,
Expand All @@ -295,7 +290,6 @@ export const createAndInit = async (
creationPath: path.join(cwd, '..'),
cwd,
e2e,
pnp,
...rest,
};

Expand All @@ -311,7 +305,9 @@ export const createAndInit = async (
if (local) {
await doTask(addLocalPackageResolutions, options);
}

await doTask(installYarn2, options);

if (e2e) {
await doTask(configureYarn2ForE2E, options, e2e);
}
Expand Down
7 changes: 7 additions & 0 deletions code/lib/cli/src/sandbox-templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,13 @@ const internalTemplates = {
},
},
},
'internal/pnp': {
...baseTemplates['react-webpack/18-ts'],
name: 'PNP (react-webpack/18-ts)',
script: 'yarn create react-app . --use-pnp',
isInternal: true,
inDevelopment: true,
},
} satisfies Record<`internal/${string}`, Template & { isInternal: true }>;

export const allTemplates: Record<TemplateKey, Template> = {
Expand Down
1 change: 1 addition & 0 deletions code/lib/core-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"pkg-dir": "^5.0.0",
"pretty-hrtime": "^1.0.3",
"resolve-from": "^5.0.0",
"resolve-pkg-maps": "^1.0.0",
"slash": "^3.0.0",
"ts-dedent": "^2.0.0"
},
Expand Down
1 change: 1 addition & 0 deletions code/lib/core-common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export * from './utils/handlebars';
export * from './utils/interpolate';
export * from './utils/interpret-files';
export * from './utils/interpret-require';
export * from './utils/safeResolve';
export * from './utils/load-custom-presets';
export * from './utils/load-main-config';
export * from './utils/load-manager-or-addons-file';
Expand Down
24 changes: 14 additions & 10 deletions code/lib/core-common/src/presets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {
PresetConfig,
Presets,
} from '@storybook/types';
import { join, parse } from 'path';
import { dirname, join, parse } from 'path';
import { loadCustomPresets } from './utils/load-custom-presets';
import { safeResolve, safeResolveFrom } from './utils/safeResolve';
import { interopRequireDefault } from './utils/interpret-require';
Expand Down Expand Up @@ -65,10 +65,12 @@ export const resolveAddonName = (
): CoreCommon_ResolvedAddonPreset | CoreCommon_ResolvedAddonVirtual | undefined => {
const resolve = name.startsWith('/') ? safeResolve : safeResolveFrom.bind(null, configDir);
const resolved = resolve(name);
const resolvedPkg = resolve(join(name, 'package.json'));

const root = resolvedPkg ? dirname(resolvedPkg) : name;

if (resolved) {
const { dir: fdir, name: fname } = parse(resolved);

if (name.match(/\/(manager|register(-panel)?)(\.(js|mjs|ts|tsx|jsx))?$/)) {
return {
type: 'virtual',
Expand All @@ -88,7 +90,9 @@ export const resolveAddonName = (
}

const checkExists = (exportName: string) => {
if (resolve(`${name}${exportName}`)) return `${name}${exportName}`;
if (resolve(join(root, exportName))) {
return join(root, exportName);
}
return undefined;
};

Expand All @@ -99,14 +103,14 @@ export const resolveAddonName = (
// Vite will be broken in such cases, because it does not process absolute paths,
// and it will try to import from the bare import, breaking in pnp/pnpm.
const absolutizeExport = (exportName: string) => {
return resolve(`${name}${exportName}`);
return resolve(join(name, exportName));
};

const managerFile = absolutizeExport(`/manager`);
const registerFile = absolutizeExport(`/register`) || absolutizeExport(`/register-panel`);
const previewFile = checkExists(`/preview`);
const previewFileAbsolute = absolutizeExport('/preview');
const presetFile = absolutizeExport(`/preset`);
const managerFile = absolutizeExport(`manager`);
const registerFile = absolutizeExport(`register`) || absolutizeExport(`register-panel`);
const previewFile = checkExists(`preview`);
const previewFileAbsolute = absolutizeExport('preview');
const presetFile = absolutizeExport(`preset`);

if (!(managerFile || previewFile) && presetFile) {
return {
Expand Down Expand Up @@ -154,7 +158,7 @@ export const resolveAddonName = (
if (resolved) {
return {
type: 'presets',
name: resolved,
name: root,
ndelangen marked this conversation as resolved.
Show resolved Hide resolved
};
}

Expand Down
1 change: 1 addition & 0 deletions code/lib/core-common/src/utils/get-storybook-info.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import path from 'path';
import fse from 'fs-extra';

import type { CoreCommon_StorybookInfo, PackageJson } from '@storybook/types';
import { getStorybookConfiguration } from './get-storybook-configuration';

Expand Down
Loading